java多執行緒之interrupted()和isInterrupted()的區別(原始碼解讀)

Airey發表於2018-09-18

關於執行緒終止方法interrupt()

由於stop()方法已經過時和廢棄,是之前JDK設計有缺陷的方法,所以我們一般使用interrupt()方法來終止執行緒,但是interrupt()方法並不像stop()方法那樣暴力終止執行緒,通俗的說使用效果並沒有for+break語句那樣,馬上就終止迴圈。呼叫interrupt()方法僅僅是在當前執行緒中打了一個停止的標記,並不是真正意義上的停止執行緒。我們先來看一個簡單的示例:

public class InterruptDemo extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
            System.out.println("i=" + (i + 1));

        }
    }

    public static void main(String[] args) {
        try {
            InterruptDemo interruptDemo = new InterruptDemo();
            interruptDemo.start();
            //讓執行緒休眠2秒
            Thread.sleep(2000);
            interruptDemo.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
    }
}

複製程式碼

執行結果如下:

java多執行緒之interrupted()和isInterrupted()的區別(原始碼解讀)
從執行結果來看,interrupt()方法並沒有終止執行緒,所以我們需要java提供的2個方法interrupted()和isInterrupted()來判斷它停止的標誌從而能正確的終止執行緒。但是這2個方法是很容易混淆的,下面我們通過原始碼解讀+測試小例子來把它們的區別和用法徹底搞清楚。

Thread.interrupted()和this.isInterrupted()的區別

判斷執行緒是否是停止狀態,java中的JDK提供了兩種方法:

Thread.interrupted():測試當前執行緒是否已經中斷
this.isInterrupted():測試執行緒是否已經中斷
複製程式碼

這兩個方法有什麼差別呢?我們先看下JDK原始碼:

/**
     * Tests whether the current thread has been interrupted.  The
     * <i>interrupted status</i> of the thread is cleared by this method.  In
     * other words, if this method were to be called twice in succession, the
     * second call would return false (unless the current thread were
     * interrupted again, after the first call had cleared its interrupted
     * status and before the second call had examined it).
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised 6.0
     */
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    /**
     * Tests whether this thread has been interrupted.  The <i>interrupted
     * status</i> of the thread is unaffected by this method.
     *
     * <p>A thread interruption ignored because a thread was not alive
     * at the time of the interrupt will be reflected by this method
     * returning false.
     *
     * @return  <code>true</code> if this thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see     #interrupted()
     * @revised 6.0
     */
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * 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);
複製程式碼

首先我們發現這個2個方法都呼叫了isInterrupted(boolean ClearInterrupted)這個方法,我們看這個方法的註解,它的意思是說如果一個執行緒已經被終止了,中斷狀態是否被重置取決於ClearInterrupted的值,即ClearInterrupted為true時,中斷狀態會被重置,為false則不會被重置。

然後我們比較這2個方法的差別,結果就很明顯了,可以看出,interrupted()是靜態方法,所以我們用Thread.interrupted()方法表示,它呼叫的是currentThread().isInterrupted(true)方法,即說明是返回當前執行緒的是否已經中斷的狀態值,而且有清理中斷狀態的機制。而isInterrupted()是一個例項方法,所以我們用this.isInterrupted()方法表示,它呼叫的是isInterrupted(false)方法,意思是返回執行緒是否已經中斷的狀態值,與Thread.interrupted()方法相反,它沒有清理中斷狀態的機制。(PS:這裡看不懂沒關係,後面會用例子驗證)

1. Thread.interrupted()

下面我們先研究一下Thread.interrupted()方法,請看下面的一個例子:

public class InterruptDemo1 extends Thread {

    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 50000; i++) {
            System.out.println("i=" + (i + 1));
        }
    }

    public static void main(String[] args) {
        InterruptDemo1 thread=new InterruptDemo1();
        thread.start();
        try {
            Thread.sleep(1000);
            thread.interrupt();
            System.out.println("是否已經停止 1?="+Thread.interrupted());
            System.out.println("是否已經停止 2?="+Thread.interrupted());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end!");
    }

複製程式碼

執行結果如下:

java多執行緒之interrupted()和isInterrupted()的區別(原始碼解讀)

從執行的結果來看,執行緒並未終止,這也就證明了interrupted()方法的解釋:測試當前執行緒是否已經中斷,這個“當前執行緒”是main,它從未中斷過,所以2個結果都是false。 哪如何讓main執行緒終止呢?

public class InterruptDemo2 {
    public static void main(String[] args) {
        Thread.currentThread().interrupt();
        System.out.println("是否停止 1?="+Thread.interrupted());
        System.out.println("是否停止 2?="+Thread.interrupted());
        System.out.println("end!");
    }
}

複製程式碼

執行結果如下:

是否停止 1?=true
是否停止 2?=false
end!
複製程式碼

從執行結果來看,方法interrupted()的確能判斷出當前執行緒是否是中斷(停止)狀態。第二個false是什麼意思呢,這就驗證了我們上面所說的中斷狀態清除的情況,就是說interrupted()會清除中斷狀態,如果連續2次呼叫該方法,則第二次會返回false值(在第一次呼叫已清除了中端狀態之後,且第二次呼叫檢驗完中斷狀態之前,當前執行緒再次中斷的情況除外)。

2. this.isInterrupted()

分析完Thread.interrupted()方法之後,我們下面再來分析this.isInterrupted()方法:

public class InterruptDemo3 extends Thread {

    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 50000; i++) {
            System.out.println("i=" + (i + 1));
        }
    }

    public static void main(String[] args) {
        InterruptDemo3 thread = new InterruptDemo3();
        thread.start();
        try {
            Thread.sleep(1000);
            thread.interrupt();
            System.out.println("是否已經停止 1?=" + thread.isInterrupted());
            System.out.println("是否已經停止 2?=" + thread.isInterrupted());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

複製程式碼

執行結果:

java多執行緒之interrupted()和isInterrupted()的區別(原始碼解讀)

從結果看,2個值都為true,說明方法isInterrupted()並沒有清除中斷狀態標誌,和我們之前分析原始碼的結果一致。

總結

  • interrupted()是static方法,呼叫的時候要用Thread.interrupted(),而isInterrupted()是例項方法,呼叫時要用執行緒的例項呼叫;
  • Thread.interrupted():測試當前執行緒是否已經是中斷狀態,執行後具有將狀態標誌清除為false的功能;
  • this.isInterrupted():測試執行緒Thread物件是否已經是中斷狀態,但不清除狀態標誌。

參考

《java多執行緒程式設計核心技術》

相關文章