abstract:今天在看到Thread類的isInterrupted方法可以獲取線程的中斷狀態(tài)于是寫(xiě)了個(gè)例子想驗(yàn)證一下:public class Interrupt { public static void main(String[] args) throws Exception { Thread t
今天在看到Thread類的isInterrupted方法可以獲取線程的中斷狀態(tài)
于是寫(xiě)了個(gè)例子想驗(yàn)證一下:
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."); } } }
內(nèi)容很簡(jiǎn)答:主線程main啟動(dòng)了一個(gè)子線程Worker,然后讓worker睡500ms,而main睡200ms,之后main調(diào)用worker線程的interrupt方法去中斷worker,worker被中斷后打印中斷的狀態(tài)。下面是執(zhí)行結(jié)果:
Worker started. Main thread stopped. Worker IsInterrupted: falseWorker stopped.
Worker明明已經(jīng)被中斷,而isInterrupted()方法竟然返回了false,為什么呢?
在stackoverflow上搜索了一圈之后,發(fā)現(xiàn)有網(wǎng)友提到:可以查看拋出InterruptedException方法的JavaDoc(或源代碼),于是我查看了Thread.sleep方法的文檔,doc中是這樣描述這個(gè)InterruptedException異常的:
InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
注意到后面這句“當(dāng)拋出這個(gè)異常的時(shí)候,中斷狀態(tài)已被清除”。所以isInterrupted()方法應(yīng)該返回false??墒怯械臅r(shí)候,我們需要isInterrupted這個(gè)方法返回true,怎么辦呢?這里就要先說(shuō)說(shuō)interrupt, interrupted和isInterrupted的區(qū)別了:
interrupt方法是用于中斷線程的,調(diào)用該方法的線程的狀態(tài)將被置為"中斷"狀態(tài)。注意:線程中斷僅僅是設(shè)置線程的中斷狀態(tài)位,不會(huì)停止線程。需要用戶自己去監(jiān)視線程的狀態(tài)為并做處理。支持線程中斷的方法(也就是線程中斷后會(huì)拋出InterruptedException的方法,比如這里的sleep,以及Object.wait等方法)就是在監(jiān)視線程的中斷狀態(tài),一旦線程的中斷狀態(tài)被置為“中斷狀態(tài)”,就會(huì)拋出中斷異常。
interrupt() merely sets the thread's interruption status. Code running in the interrupted thread can later poll the interrupted status to see if it has been requested to stop what it is doing
再來(lái)看看interrupted方法的實(shí)現(xiàn):
public static boolean interrupted() { return currentThread().isInterrupted(true); }
和isInterrupted的實(shí)現(xiàn):
public boolean isInterrupted() { return isInterrupted(false); }
這兩個(gè)方法一個(gè)是static的,一個(gè)不是,但實(shí)際上都是在調(diào)用同一個(gè)方法,只是interrupted方法傳入的參數(shù)為true,而inInterrupted傳入的參數(shù)為false。那么這個(gè)參數(shù)到底是什么意思呢?來(lái)看下這個(gè)isInterrupted(boolean)方法的實(shí)現(xiàn):
/** * 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);
這是一個(gè)native方法,看不到源碼沒(méi)有關(guān)系,參數(shù)名字ClearInterrupted已經(jīng)清楚的表達(dá)了該參數(shù)的作用----是否清除中斷狀態(tài)。方法的注釋也清晰的表達(dá)了“中斷狀態(tài)將會(huì)根據(jù)傳入的ClearInterrupted參數(shù)值確定是否重置”。所以,靜態(tài)方法interrupted將會(huì)清除中斷狀態(tài)(傳入的參數(shù)ClearInterrupted為true),而實(shí)例方法isInterrupted則不會(huì)(傳入的參數(shù)ClearInterrupted為false)。
回到剛剛的問(wèn)題:很明顯,如果要isInterrupted這個(gè)方法返回true,通過(guò)在調(diào)用isInterrupted方法之前再次調(diào)用interrupt()方法來(lái)恢復(fù)這個(gè)中斷的狀態(tài)即可:
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(); //再次調(diào)用interrupt方法中斷自己,將中斷狀態(tài)設(shè)置為“中斷” 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."); } }
執(zhí)行結(jié)果:
Worker started. Main thread stopped. Worker IsInterrupted: trueWorker IsInterrupted: trueStatic Call: true---------After Interrupt Status Cleared---------- Static Call: falseWorker IsInterrupted: falseWorker IsInterrupted: falseWorker stopped.
從執(zhí)行結(jié)果也可以看到,前兩次調(diào)用isInterrupted方法都返回true,說(shuō)明isInterrupted方法不會(huì)改變線程的中斷狀態(tài),而接下來(lái)調(diào)用靜態(tài)的interrupted()方法,第一次返回了true,表示線程被中斷,第二次則返回了false,因?yàn)榈谝淮握{(diào)用的時(shí)候已經(jīng)清除了中斷狀態(tài)。最后兩次調(diào)用isInterrupted()方法就肯定返回false了。
那么,在什么場(chǎng)景下,我們需要在catch塊里面中斷線程(重置中斷狀態(tài))呢?
答案是:如果不能拋出InterruptedException(就像這里的Thread.sleep語(yǔ)句放在了Runnable的run方法中,這個(gè)方法不允許拋出任何受檢查的異常),但又想告訴上層調(diào)用者這里發(fā)生了中斷的時(shí)候,就只能在catch里面重置中斷狀態(tài)了。
If you catch InterruptedException but cannot rethrow it, you should preserve evidence that the interruption occurred so that code higher up on the call stack can learn of the interruption and respond to it if it wants to. This task is accomplished by calling interrupt() to "reinterrupt" the current thread, as shown in Listing 3.
Listing 3: Restoring the interrupted status after catching InterruptedException
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(); } } }
那么問(wèn)題來(lái)了:為什么要在拋出InterruptedException的時(shí)候清除掉中斷狀態(tài)呢?
這個(gè)問(wèn)題沒(méi)有找到官方的解釋,估計(jì)只有Java設(shè)計(jì)者們才能回答了。但這解釋似乎比較合理:一個(gè)中斷應(yīng)該只被處理一次(你catch了這個(gè)InterruptedException,說(shuō)明你能處理這個(gè)異常,你不希望上層調(diào)用者看到這個(gè)中斷)。