嚼爛interrupt,interrupted和isInterrupted
前言
前面在講java中的執行緒中斷機制中,我們提到了用stop和suspend來強制終止執行緒的不安全性,當時也提到了interrupt方法。這裡再和大家一起學習下Java多執行緒中的interrupt,interrupted和isInterrupted。
interrupt的伸冤之路
首先對於interrupt方法,他是用於中斷執行緒的,呼叫該方法的執行緒的狀態將被置為"中斷"狀態。注意:執行緒中斷僅僅是設定執行緒的中斷狀態位,不會停止執行緒。需要使用者自己去監視執行緒的狀態為並做處理。支援執行緒中斷的方法(也就是執行緒中斷後會丟擲InterruptedException的方法,比如說sleep,wait等方法)就是在監視執行緒的中斷狀態,一旦執行緒的中斷狀態被置為“中斷狀態”,就會丟擲中斷異常。
接下來,我們再來看看isInterrupted。isInterrupted是用來判斷執行緒是否處於執行緒中斷狀態。如果是返回true,否則返回false。就如同下圖中介紹的
下面我們來看個例子,看看interrupt如何被冤枉了
public class Interrupt { public static void main(String[] args) throws Exception { Thread t = new Thread(new Worker()); t.start(); Thread.sleep(200); t.interrupt(); System.out.println("Main thread stopped."); } public static class Worker implements Runnable { public void run() { System.out.println("Worker started."); try { Thread.sleep(500); } catch (InterruptedException e) { System.out.println("Worker IsInterrupted:" + Thread.currentThread().isInterrupted()); } System.out.println("Worker stopped."); } } }
上面程式碼是主執行緒main啟動了一個子執行緒Worker,然後讓worker睡500ms,而main睡200ms,之後main呼叫worker執行緒的interrupt方法去中斷worker,worker被中斷後列印中斷的狀態。下面是執行結果:
Worker started.
Main thread stopped.
Worker IsInterrupted: false
Worker stopped.
看到上面的結果。估計認真看完上面對於interrupt和isInterrupted介紹的同學,一副問號臉,interrupt不是說好的修改執行緒為中斷狀態麼,為啥isInterrupted返回的還是false?interrupt不要面子的?
稍安勿躁,其實上面的問題是出在了InterruptedException上面。我們先來看一段來自InterruptedException 的獨白
InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
原來是InterruptedException 這小子搞得鬼。由於呼叫了Thread.sleep(500)方法,而sleep()方法上面也說到會丟擲InterruptedException 異常,這就導致了interrupt設定的中斷狀態被清除了,所以isInterrupted返回false就很好解釋了。
interrupted和isInterrupted的恩恩怨怨
我們來仔細看看這兩位的介紹,首先是interrupted
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
再是isInterrupted
public boolean isInterrupted() {
return isInterrupted(false);
}
這兩個方法一個是static的,一個不是,但實際上都是在呼叫同一個方法,只是interrupted方法傳入的引數為true,而inInterrupted傳入的引數為false。所以要看清楚interrupted和isInterrupted的真實面目,我們還是要繼續往下看
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
這是一個native方法,看不到原始碼沒有關係,引數名字ClearInterrupted已經清楚的表達了該引數的作用----是否清除中斷狀態。方法的註釋也清晰的表達了“中斷狀態將會根據傳入的ClearInterrupted引數值確定是否重置”。所以,靜態方法interrupted將會清除中斷狀態(傳入的引數ClearInterrupted為true),而例項方法isInterrupted則不會(傳入的引數ClearInterrupted為false)。
為了驗證上面的區別,我們接著看下面的例子
public class Interrupt {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Worker());
t.start();
Thread.sleep(200);
t.interrupt();
System.out.println("Main thread stopped.");
}
public static class Worker implements Runnable {
public void run() {
System.out.println("Worker started.");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread curr = Thread.currentThread();
//再次呼叫interrupt方法中斷自己,將中斷狀態設定為“中斷”
curr.interrupt();
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
System.out.println("Static Call: " + Thread.interrupted());//clear status
System.out.println("---------After Interrupt Status Cleared----------");
System.out.println("Static Call: " + Thread.interrupted());
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
}
System.out.println("Worker stopped.");
}
}
}
執行結果:
Worker started.
Main thread stopped.
Worker IsInterrupted: true
Worker IsInterrupted: true
Static Call: true
---------After Interrupt Status Cleared----------
Static Call: false
Worker IsInterrupted: false
Worker IsInterrupted: false
Worker stopped.
從執行結果也可以看到,前兩次呼叫isInterrupted方法都返回true,說明isInterrupted方法不會改變執行緒的中斷狀態,而接下來呼叫靜態的interrupted()方法,第一次返回了true,表示執行緒被中斷,第二次則返回了false,因為第一次呼叫的時候已經清除了中斷狀態。最後兩次呼叫isInterrupted()方法就肯定返回false了。
上面的程式碼中我們看到,我們在catch塊中重新呼叫了interrupt方法,重置了中斷狀態,所以後面的isInterrupt方法才會返回true。那麼我們在什麼情況下需要在catch塊中去重置中斷狀態呢?其實如果我們的方法中不能丟擲InterruptedException(就像這裡的Thread.sleep語句放在了Runnable的run方法中,這個方法不允許丟擲任何受檢查的異常),但又想告訴上層呼叫者這裡發生了中斷的時候,就只能在catch裡面重置中斷狀態了。就如同下面的例子
public class TaskRunner implements Runnable {
private BlockingQueue<Task> queue;
public TaskRunner(BlockingQueue<Task> queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
}
總結
上面介紹了interrupt,interrupted和isInterrupt方法線上程中斷的區別。只為大家更好的理解這三者的關係和區別。本文參考(借鑑)了 interrupt、interrupted和isInterrupted的區別 的內容,我用自己的語言稍微組織了下,便於自己理解學習。
原文連結:https://blog.csdn.net/hj7jay/article/details/53462553#