理解Java執行緒的中斷

壹頁書發表於2016-07-11
執行緒中斷三方法

public void Thread.interrupt() // 中斷執行緒
public boolean Thread.isInterrupted() // 判斷是否被中斷
public static boolean Thread.interrupted() // 判斷是否被中斷,並清除當前中斷狀態

轉載自:
http://blog.csdn.net/sunxing007/article/details/9123363

一個執行緒在未正常結束之前, 被強制終止是很危險的事情. 因為它可能帶來完全預料不到的嚴重後果. 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了.
那麼不能直接把一個執行緒搞掛掉, 但有時候又有必要讓一個執行緒死掉, 或者讓它結束某種等待的狀態 該怎麼辦呢? 優雅的方法就是, 給那個執行緒一箇中斷訊號, 讓它自己決定該怎麼辦. 比如說, 在某個子執行緒中為了等待一些特定條件的到來, 你呼叫了Thread.sleep(10000), 預期執行緒睡10秒之後自己醒來, 但是如果這個特定條件提前到來的話, 你怎麼通知一個在睡覺的執行緒呢? 又比如說, 主執行緒透過呼叫子執行緒的join方法阻塞自己以等待子執行緒結束, 但是子執行緒執行過程中發現自己沒辦法在短時間內結束, 於是它需要想辦法告訴主執行緒別等我了. 這些情況下, 就需要中斷. 
中斷是透過呼叫Thread.interrupt()方法來做的. 這個方法透過修改了被呼叫執行緒的中斷狀態來告知那個執行緒, 說它被中斷了. 對於非阻塞中的執行緒, 只是改變了中斷狀態, 即Thread.isInterrupted()將返回true; 對於可取消的阻塞狀態中的執行緒, 比如等待在這些函式上的執行緒, Thread.sleep(), Object.wait(), Thread.join(), 這個執行緒收到中斷訊號後, 會丟擲InterruptedException, 同時會把中斷狀態置回為false.
下面的程式會演示對非阻塞中的執行緒中斷:
  1. public class Thread3 extends Thread{    
  2.     public void run(){    
  3.         while(true){    
  4.             if(Thread.interrupted()){    
  5.                 System.out.println("Someone interrupted me.");    
  6.             }    
  7.             else{    
  8.                 System.out.println("Going...");    
  9.             }    
  10.             long now = System.currentTimeMillis();    
  11.             while(System.currentTimeMillis()-now<1000){    
  12.                 // 為了避免Thread.sleep()而需要捕獲InterruptedException而帶來的理解上的困惑,    
  13.                 // 此處用這種方法空轉1秒    
  14.             }    
  15.         }    
  16.     }    
  17.         
  18.     public static void main(String[] args) throws InterruptedException {    
  19.         Thread3 t = new Thread3();    
  20.         t.start();    
  21.         Thread.sleep(3000);    
  22.         t.interrupt();    
  23.     }    
  24. }    

下面的程式演示的是子執行緒通知父執行緒別等它了:
  1. public class Thread4 extends Thread {    
  2.     private Thread parent;    
  3.     public Thread4(Thread parent){    
  4.         this.parent = parent;    
  5.     }    
  6.         
  7.     public void run() {    
  8.         while (true) {    
  9.             System.out.println("sub thread is running...");    
  10.             long now = System.currentTimeMillis();    
  11.             while (System.currentTimeMillis() - now < 2000) {    
  12.                 // 為了避免Thread.sleep()而需要捕獲InterruptedException而帶來的理解上的困惑,    
  13.                 // 此處用這種方法空轉2秒    
  14.             }    
  15.             parent.interrupt();    
  16.         }    
  17.     }    
  18.         
  19.     public static void main(String[] args){    
  20.         Thread4 t = new Thread4(Thread.currentThread());    
  21.         t.start();    
  22.         try {    
  23.             t.join();    
  24.         } catch (InterruptedException e) {    
  25.             System.out.println("Parent thread will die...");    
  26.         }    
  27.     }    
  28. }    

中斷狀態可以透過 Thread.isInterrupted()來讀取,並且可以透過一個名為 Thread.interrupted()的靜態方法讀取和清除狀態(即呼叫該方法結束之後, 中斷狀態會變成false)。
由於處於阻塞狀態的執行緒 被中斷後丟擲exception並置回中斷狀態, 有時候是不利的, 因為這個中斷狀態可能會作為別的執行緒的判斷條件, 所以穩妥的辦法是在處理exception的地方把狀態復位:
  1. boolean interrupted = false;    
  2. try {    
  3.     while (true) {    
  4.         try {    
  5.             return blockingQueue.take();    
  6.         } catch (InterruptedException e) {    
  7.             interrupted = true;    
  8.         }    
  9.     }    
  10. finally {    
  11.     if (interrupted){    
  12.         Thread.currentThread().interrupt();    
  13.     }    
  14. }   

當程式碼呼叫中須要丟擲一個InterruptedException, 你可以選擇把中斷狀態復位, 也可以選擇向外丟擲InterruptedException, 由外層的呼叫者來決定.

不是所有的阻塞方法收到中斷後都可以取消阻塞狀態, 輸入和輸出流類會阻塞等待 I/O 完成,但是它們不丟擲 InterruptedException,而且在被中斷的情況下也不會退出阻塞狀態. 

嘗試獲取一個內部鎖的操作(進入一個 synchronized 塊)是不能被中斷的,但是 ReentrantLock 支援可中斷的獲取模式即 tryLock(long time, TimeUnit unit)。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2121861/,如需轉載,請註明出處,否則將追究法律責任。

相關文章