1. 程式人生 > >嚼爛interrupt,interrupted和isInterrupted

嚼爛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#