什么時候要用到中斷:
比如你開了生產者Producer和消費者Consumer兩個線程,並用一個同步隊列放置Porducer生產的和Consumer消費的產品。在Consumer中開啟一個Producer線程,並且當Consumer不想消費時,可以隨時結束掉Producer線程。如果不使用中斷,可以使用一個boolean canceled標志,在Producer中循環檢查該標志,如果該標志為true則退出。
try{
while(! cancelled){
product = Producer.produce();
blockingQueue.put(product);
}
}catch(InterruptedException){}
可是當blockingQueue被填滿,Producer線程被堵塞在put()方法時,Consumer將cancelled設為true,並停止消費。在這種情況下,Producer線程將一直阻塞在put()方法處,永遠檢測不到cancel被置為true。
BlockingQueue.put()方法是一個阻塞庫方法,它可以及時檢測到線程的中斷狀態。因此如果在Consumer中使用interrupt()方法中斷Producer,那么即使Producer阻塞在put()處了,也能及時響應中斷請求,結束生產線程。
線程中斷到底是什么:
“線程中斷是一種協作機制,線程可以通過這種機制來通知另一個線程,告訴它在合適的或者可能的情況下停止當前工作。”
每個線程都有一個boolean類型的中斷變量,並且在Thread類中有三個方法,分別是
public class Thread{
public void interrupt(){...}
public boolean isInterrupted(){...}
public static boolean interrupted(){...}
...
}
其他線程可以調用線程的interrupt()方法,通過將中斷變量設為true來中斷該線程;isInterrupted()方法返回線程的中斷變量狀態,但不會改變其狀態值;而interrupted()方法會先清除中斷狀態(即將中斷變量設為false),並返回之前的中斷狀態。
System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
System.out.println("thread is interrupted: " + Thread.interrupted());
System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());
輸出結果:
thread is interrupted: false thread is interrupted: true thread is interrupted: true thread is interrupted: false
可以看到,在使用interrupted()類方法后,線程的中斷狀態變為false。
怎么使用中斷機制
其實,中斷操作(即調用線程的interrupte()方法)並不是會停止一個正在運行的線程,它只是把線程里邊的中斷變量設置為true,向這個線程發出一個中斷請求,而線程怎么反應,則是線程自己的事情。它可以完全不理會中斷,照常執行。舉個栗子
public class interruptTest {
private static class Endless extends Thread{
public void run(){
int i=0;
while (i<500){
i++;
System.out.println(i);
}
}
}
public static void main(String[] args) throws InterruptedException{
Thread endless = new Endless();
endless.start();
Thread.sleep(1);
endless.interrupt();
System.out.println("main: I have interrupted endless");
}
}
這段代碼的輸出為:


雖然主線程試圖中斷endless,但它不為所動,還是把循環執行完了,正常退出。
但不推薦線程進行如此任性的操作。當線程被發現被中斷時,要不然把異常拋出(停止運行,throws InterruptedException);要不然推遲中斷請求(繼續進行操作),但需要把中斷信息傳遞給調用該線程的上層調用者。對於第二種情況,來看一下下面的例子:
class Consumer extends Thread{
public void run(){
boolean interrupt = false;
try {
while (true){
try {
consume(blockingQueue.take());
return;
}catch (InterruptedException e){
interrupt = true;
}
}
}finally {
if (interrupt){
Thread.currentThread().interrupt();
}
}
}
}
線程Consumer會執着地等到消費了一個從阻塞隊列中拿到的產品后,才結束執行,即使該線程的中斷狀態被設為true,且被blockingQueue.take()方法發現,並拋出InterruptedException后。但它在finally中將線程的中斷狀態重新置為了true,以保證調用Consumer的上層線程能知道該線程被中斷過。
但現在出現了一個問題是,為什么在finally中要調用interrupt()方法重新把線程中斷狀態置為true呢。講道理如果blockingQueue.take()方法發現了中斷,那就證明線程中斷狀態已經是true了啊。原因就在於這個blockingQueue的take()方法。在阻塞庫方法,如Thread.sleep(), Object.wait(),以及這里的BlockingQueue.take()方法中(其實從BlockingQueue.take()的源碼中可以看到,在take()方法中調用了Object.wait()),都及時地響應當前線程的中斷請求,並且在響應時執行兩個操作:一是清除中斷狀態,二是拋出InterruptedException。清除中斷狀態就是把中斷狀態重新置為false,這就解釋了為什么在finally中要重新調用interrupt()方法。
什么是恢復中斷
現在可以來解釋一下什么叫恢復中斷,我理解的恢復中斷,並不是把線程從BLOCKED或WAITING等狀態中解救出來(本來中斷也不是這么個意思),而是把線程的中斷狀態變量重新設置為True。
寫在后面:
看《Java並發編程實戰》時看到第7章開頭就看不明白,發現自己其實一直不懂中斷到底是個什么東西,一直傻傻地認為中斷和線程阻塞是一個意思。。。然后查了一些博客,並且把不懂的地方反復看了好幾遍,才稍稍有些明白了。寫博客其實就是捋捋自己的思路,順便記錄一下,如有錯誤懇請指出。
