Java中線程中斷機(jī)制的原理是什么

本篇文章為大家展示了Java中線程中斷機(jī)制的原理是什么,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供鲅魚圈網(wǎng)站建設(shè)、鲅魚圈做網(wǎng)站、鲅魚圈網(wǎng)站設(shè)計、鲅魚圈網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、鲅魚圈企業(yè)網(wǎng)站模板建站服務(wù),十年鲅魚圈做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

Thread.interrupt真的能中斷線程嗎

在平時的開發(fā)過程中,相信都會使用到多線程,在使用多線程時,大家也會遇到各種各樣的問題,今天我們就來說說一個多線程的問題——線程中斷。在 java中啟動線程非常容易,大多數(shù)情況下我是讓一個線程執(zhí)行完自己的任務(wù)然后自己停掉,但是有時候我們需要取消某個操作,比如你在網(wǎng)絡(luò)下載時,有時候需 要取消下載。實現(xiàn)線程的安全中斷并不是一件容易的事情,因為Java并不支持安全快速中斷線程的機(jī)制,這里估計很多同學(xué)就會說了,java不是提供了Thread.interrupt 方法中斷線程嗎,好吧,我們今天就從這個方法開始說起。

但是調(diào)用此方法線程真的會停止嗎?我們寫個demo看看就知道了。

public class Main {   private static final String TAG = "Main";   public static void main(String[] args) {     Thread t=new Thread(new NRunnable());     t.start();     System.out.println("is start.......");     try {       Thread.sleep(3000);     } catch (InterruptedException e) {      }      t.interrupt();     System.out.println("is interrupt.......");    }    public static class NRunnable implements Runnable   {      @Override     public void run() {       while(true)       {         System.out.println("我沒有種中斷");         try {           Thread.sleep(1000);         } catch (InterruptedException e) {          }       }     }    } }

如果interrutp方法能夠中斷線程,那么在打印了is interrupt…….之后應(yīng)該是沒有l(wèi)og了,我們看看執(zhí)行結(jié)果吧

is start.......
我沒有種中斷
我沒有種中斷
我沒有種中斷
我沒有種中斷
我沒有種中斷
is interrupt.......
我沒有種中斷
我沒有種中斷
我沒有種中斷
我沒有種中斷
我沒有種中斷
....

通過結(jié)果可以發(fā)現(xiàn)子線程并沒有中斷

所以 Thread.interrupt() 方法并不能中斷線程,該方法僅僅告訴線程外部已經(jīng)有中斷請求,至于是否中斷還取決于線程自己。在Thread類中除了interrupt() 方法還有另外兩個非常相似的方法:interrupted 和 isInterrupted 方法,下面來對這幾個方法進(jìn)行說明:

  • interrupt 此方法是實例方法,用于告訴此線程外部有中斷請求,并且將線程中的中斷標(biāo)記設(shè)置為true

  • interrupted 此方法是類方法,測試當(dāng)前線程是否已經(jīng)中斷。線程的中斷狀態(tài) 由該方法清除。換句話說,如果連續(xù)兩次調(diào)用該方法,則第二次調(diào)用將返回 false(在***次調(diào)用已清除了其中斷狀態(tài)之后,且第二次調(diào)用檢驗完中斷狀態(tài)前,當(dāng)前線程再次中斷的情況除外)。

  • isInterrupted 此方法是實例方法測試線程是否已經(jīng)中斷。線程的中斷狀態(tài) 不受該方法的影響。 線程中斷被忽略,因為在中斷時不處于活動狀態(tài)的線程將由此返回 false 的方法反映出來

處理線程中斷的常用方法

設(shè)置取消標(biāo)記

還是用上面的例子,只不過做了些修改

public static void main(String[] args) {     NRunnable run=new NRunnable();     Thread t=new Thread(run);     t.start();     System.out.println("is start.......");     try {       Thread.sleep(3000);     } catch (InterruptedException e) {      }     run.cancel();     System.out.println("cancel ..."+System.currentTimeMillis());   }    public static class NRunnable implements Runnable   {     public boolean isCancel=false;      @Override     public void run() {       while(!isCancel)       {         System.out.println("我沒有種中斷");         try {           Thread.sleep(10000);         } catch (InterruptedException e) {          }       }       System.out.println("我已經(jīng)結(jié)束了..."+System.currentTimeMillis());     }      public void cancel()     {       this.isCancel=true;     }    }

執(zhí)行結(jié)果如下:

is start.......
我沒有種中斷
cancel ...1438396915809
我已經(jīng)結(jié)束了...1438396922809

通過結(jié)果,我們發(fā)現(xiàn)線程確實已經(jīng)中斷了,但是細(xì)心的同學(xué)應(yīng)該發(fā)現(xiàn)了一個問題,調(diào)用cancel方法和***線程執(zhí)行完畢之間隔了好幾秒的時間,也就是說線程不是立馬中斷的,我們下面來分析一下原因:

子線程退出的條件是while循環(huán)結(jié)束,也就是cancel標(biāo)示設(shè)置為true,但是當(dāng)我們調(diào)用cancel方法將calcel標(biāo)記設(shè)置為true 時,while循環(huán)里面有一個耗時操作(sleep方法模擬),只有等待耗時操作執(zhí)行完畢后才會去檢查這個標(biāo)記,所以cancel方法和線程退出中間有時 間間隔。

通過interrupt 和 isinterrupt 方法來中斷線程

public static void main(String[] args) {     Thread t=new NThread();     t.start();     System.out.println("is start.......");     try {       Thread.sleep(3000);     } catch (InterruptedException e) {      }     System.out.println("start interrupt..."+System.currentTimeMillis());     t.interrupt();     System.out.println("end interrupt ..."+System.currentTimeMillis());   }    public static class NThread extends Thread   {      @Override     public void run() {       while(!this.isInterrupted())       {         System.out.println("我沒有種中斷");         try {           Thread.sleep(10000);         } catch (InterruptedException e) {           Thread.currentThread().interrupt();         }       }       System.out.println("我已經(jīng)結(jié)束了..."+System.currentTimeMillis());     }    } }

運行結(jié)果如下:

is start.......
我沒有種中斷
start interrupt...1438398800110
我已經(jīng)結(jié)束了...1438398800110
end interrupt ...1438398800110

這次是立馬中斷的,但是這種方法是由局限性的,這種方法僅僅對于會拋出InterruptedException 異常的任務(wù)時有效的,比如java中的sleep、wait 等方法,對于不會拋出這種異常的任務(wù)其效果其實和***種方法是一樣的,都會有延遲性,這個例子中還有一個非常重要的地方就是cache語句中,我們調(diào)用了Thread.currentThread().interrupt() 我們把這句代碼去掉,運行你會發(fā)現(xiàn)這個線程無法終止,因為在拋出InterruptedException 的同時,線程的中斷標(biāo)志被清除了,所以在while語句中判斷當(dāng)前線程是否中斷時,返回的是false.針對InterruptedException 異常,我想說的是:一定不能再catch語句塊中什么也不干,如果你實在不想處理,你可以將異常拋出來,讓調(diào)用拋異常的方法也成為一個可以拋出InterruptedException 的方法,如果自己要捕獲此異常,那么***在cache語句中調(diào)用 Thread.currentThread().interrupt(); 方法來讓高層只要中斷請求并處理該中斷。

對于上述兩種方法都有其局限性,***種方法只能處理那種工作量不大,會頻繁檢查循環(huán)標(biāo)志的任務(wù),對于第二種方法適合用于拋出InterruptedException的代碼。也就是說***種和第二種方法支持的是支持中斷的線程任務(wù),那么不支持中斷的線程任務(wù)該怎么做呢。

例如 如果一個線程由于同步進(jìn)行I/O操作導(dǎo)致阻塞,中斷請求不會拋出InterruptedException ,我們該如何中斷此線程呢。

處理不支持中斷的線程中斷的常用方法

改寫線程的interrupt方法

public static class ReaderThread extends Thread {    public static final int BUFFER_SIZE=512;    Socket socket;    InputStream is;     public ReaderThread(Socket socket) throws IOException    {      this.socket=socket;      is=this.socket.getInputStream();    }     @Override   public void interrupt() {      try      {        socket.close();      }catch(IOException e)      {       }finally      {        super.interrupt();      }     super.interrupt();   }    @Override   public void run() {      try      {        byte[]buf=new byte[BUFFER_SIZE];        while(true)        {          int count=is.read(buf);          if(count<0)            break;          else if(count>0)          {           }        }      }catch(IOException e)      {       }   } } }

例如在上面的例子中,改寫了Thread的interrupt 方法,當(dāng)調(diào)用interrupt 方法時,會關(guān)閉socket,如果此時read方法阻塞,那么會拋出IOException 此時線程任務(wù)也就結(jié)束了。

以上方法是通過改寫線程的interrupt 方法實現(xiàn),那么對于使用線程池的任務(wù)該怎么中斷呢。

改寫線程池的newTaskFor方法

通常我們向線程池中加入一個任務(wù)采用如下形式:

Future<?> future=executor.submit(new Runnable(){       @Override       public void run() {        }     });  取消任務(wù)時,調(diào)用的是future的cancel方法,其實在cancel方法中調(diào)用的是線程的interrupt方法。所以對于不支持中斷的任務(wù)cancel也是無效的,下面我們看看submit方法里面干了上面吧      public Future<?> submit(Runnable task) {         if (task == null) throw new NullPointerException();         RunnableFuture<Void> ftask = newTaskFor(task, null);         execute(ftask);         return ftask;     }  這里調(diào)用的是AbstractExecutorService 的newTaskFor方法,那么我們能不能改寫ThreadPoolExecutor的newTaskFor方法呢,接下來看我在處理吧  定義一個基類,所有需要取消的任務(wù)繼承這個基類  public interface CancelableRunnable<T> extends Runnable {    public void cancel();   public RunnableFuture<T> newTask();  }  將上面的ReaderThread改為繼承這個類   public static class ReaderThread implements CancelableRunnable<Void>   {     public static final int BUFFER_SIZE=512;     Socket socket;     InputStream is;      public ReaderThread(Socket socket) throws IOException     {       this.socket=socket;       is=this.socket.getInputStream();     }      @Override    public void run() {       try       {         byte[]buf=new byte[BUFFER_SIZE];         while(true)         {           int count=is.read(buf);           if(count<0)             break;           else if(count>0)           {            }         }       }catch(IOException e)       {        }    }      @Override     public void cancel() {       try {         socket.close();       } catch (IOException e) {        }     }      @Override     public RunnableFuture<Void> newTask() {       return new FutureTask<Void>(this,null)           {             @Override             public boolean cancel(boolean mayInterruptIfRunning) {               return super.cancel(mayInterruptIfRunning);               if(ReaderThread.this instanceof CancelableRunnable))               {                 ((CancelableRunnable)(ReaderThread.this)).cancel();               }else               {                 super.cancel(mayInterruptIfRunning);               }             }           };      } }

當(dāng)你調(diào)用future的cancel的方法時,它會關(guān)閉socket,最終導(dǎo)致read方法異常,從而終止線程任務(wù)。

上述內(nèi)容就是Java中線程中斷機(jī)制的原理是什么,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)站欄目:Java中線程中斷機(jī)制的原理是什么
文章位置:http://muchs.cn/article36/gesspg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號、移動網(wǎng)站建設(shè)、營銷型網(wǎng)站建設(shè)、域名注冊網(wǎng)站設(shè)計公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化