JAVA 中interrupt、interrupted和isInterrupted的區別

首先,咱們說明下三個方法的功能java

  • interrupt() 向當前調用者線程發出中斷信號
  • isinterrupted() 查看當前中斷信號是true仍是false
  • interrupted() 是靜態方法,查看返回當前中斷信號並將中斷信號復位

interrupt()方法,可以將處於阻塞狀態或等待狀態(如wait()、 sleep() 、join()方法)的線程從阻塞狀態跳出ide

今天在看到Thread類的isInterrupted方法能夠獲取線程的中斷狀態:this

因而寫了個例子想驗證一下:線程

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.

Worker明明已經被中斷,而isInterrupted()方法居然返回了false,爲何呢?
 code

在stackoverflow上搜索了一圈以後,發現有網友提到:能夠查看拋出InterruptedException方法的JavaDoc(或源代碼),因而我查看了Thread.sleep方法的文檔,doc中是這樣描述這個InterruptedException異常的:blog

InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.  

注意到後面這句「當拋出這個異常的時候,中斷狀態已被清除」。因此isInterrupted()方法應該返回false。但是有的時候,咱們須要isInterrupted這個方法返回true,怎麼辦呢?這裏就要先說說interrupt, interrupted和isInterrupted的區別了:

interrupt方法是用於中斷線程的,調用該方法的線程的狀態將被置爲"中斷"狀態。注意:線程中斷僅僅是設置線程的中斷狀態位,不會中止線程。須要用戶本身去監視線程的狀態爲並作處理。支持線程中斷的方法(也就是線程中斷後會拋出InterruptedException的方法,好比這裏的sleep,以及Object.wait等方法)就是在監視線程的中斷狀態,一旦線程的中斷狀態被置爲「中斷狀態」,就會拋出中斷異常。文檔

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

再來看看interrupted方法的實現:源碼

public static boolean interrupted() {  
    return currentThread().isInterrupted(true);  
}

和isInterrupted的實現:it

public boolean isInterrupted() {  
    return isInterrupted(false);  
}

這兩個方法一個是static的,一個不是,但實際上都是在調用同一個方法,只是interrupted方法傳入的參數爲true,而inInterrupted傳入的參數爲false。那麼這個參數究竟是什麼意思呢?來看下這個isInterrupted(boolean)方法的實現:

/** 
 * 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)。

回到剛剛的問題:很明顯,若是要isInterrupted這個方法返回true,經過在調用isInterrupted方法以前再次調用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) {  
                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塊裏面中斷線程(重置中斷狀態)呢?
答案是:若是不能拋出InterruptedException(就像這裏的Thread.sleep語句放在了Runnable的run方法中,這個方法不容許拋出任何受檢查的異常),但又想告訴上層調用者這裏發生了中斷的時候,就只能在catch裏面重置中斷狀態了。
如下內容來自:Dealing with InterruptedException

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. 

若是捕捉到InterruptedException但不能從新拋出,則應保留中斷髮生的證據,以便調用堆棧上的較高層代碼能夠了解中斷,並在須要時做出響應。這個任務是經過調用interrupt()來實現對當前線程的「從新中斷」,如清單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();  
         }  
    }  
}

那麼問題來了:爲何要在拋出InterruptedException的時候清除掉中斷狀態呢? 這個問題沒有找到官方的解釋,估計只有Java設計者們才能回答了。但這裏的解釋彷佛比較合理:一箇中斷應該只被處理一次(你catch了這個InterruptedException,說明你能處理這個異常,你不但願上層調用者看到這個中斷)。

相關文章
相關標籤/搜索