Java多線程之interrupt()的深度研究


  近期學習Java多線程的中斷機制,網上的帖子說得很淺,並沒深究其原理。看了Java源碼,對Java的中斷機制有了略深入的理解,在這篇文章中向感興趣的網友分享下。這篇文章主要通過一個典型例子對中斷機制進行剖析。

  一:一些概念和重要方法

  interrupt status(中斷狀態):請記住這個術語,中斷機制就是圍繞着這個字段來工作的。在Java源碼中代表中斷狀態的字段是:private volatile Interruptible blocker;對“Interruptible”這個類不需要深入分析,對於“blocker”變量有以下幾個操作。

  1.默認blocker=null; ®1

  2.調用方法“interrupt0();”將會導致“該線程的中斷狀態將被設置(JDK文檔中術語)”。®2

  3.再次調用“interrupt0();”將會導致“其中斷狀態將被清除(同JDK文檔中術語)”®3

  注:這三點很重要,接下來文章中會用來®1®2®3代替。

明白了第一點來看下文檔中對於中斷線程相關方法的描述。

  1.public void interrupt();

    中斷線程。如果線程在調用 Object 類的 wait()wait(long)wait(long, int) 方法,或者該類的 join()join(long)join(long, int)sleep(long)sleep(long, int) 方法

  過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。 

  2.public static boolean interrupted();

    測試當前線程是否已經中斷。線程的中斷狀態 由該方法清除。線程中斷被忽略,因為在中斷時不處於活動狀態的線程將由此返回 false 的方法反映出來。

      返回:
    如果當前線程已經中斷,則返回 true;否則返回 false
      另請參見:
isInterrupted()

  3.public boolean isInterrupted();

    測試線程是否已經中斷。線程的中斷狀態 不受該方法的影響。線程中斷被忽略,因為在中斷時不處於活動狀態的線程將由此返回 false 的方法反映出來。

  返回:如果該線程已經中斷,則返回 true;否則返回 false

  另請參見:interrupted()

  <!--來自JDK API文檔-->

    以上三段中關於線程的中斷狀態 由該方法清除的描述,在源碼層面就是®3調用。

 接下來說一下"interrupted()"和"isInterrupted()"兩個方法的相同點和不同點。在這之前看一下源碼中兩個方法的代碼,如下:

 1 public static boolean interrupted() {  2     return currentThread().isInterrupted(true);  3  }  4 public boolean isInterrupted() {  5     return isInterrupted(false);  6  }  7  /**
 8  * Tests if some Thread has been interrupted. The interrupted state  9  * is reset or not based on the value of ClearInterrupted that is 10  * passed. 11      */
12 private native boolean isInterrupted(boolean ClearInterrupted);

  相同點都是判斷線程的interrupt status是否被設置,若被設置返回true,否則返回false.區別有兩點:一:前者是static方法,調用者是current thread,而后者是普通方法,調用者是this current.二:它們其實都調用了Java中的一個native方法isInterrupted(boolean ClearInterrupted); 不同的是前者傳入了參數true,后者傳入了false.意義就是:前者將清除線程的interrupt state(®3),調用后者線程的interrupt state不受影響。

二:例子。

  接下來看一個例子,這個例子說明了兩個問題。1.調用interrupt()方法並不會中斷一個正在運行的線程.2.若調用sleep()而使線程處於阻塞狀態,這時調用interrupt()方法,會拋出InterruptedException,從而使線程提前結束阻塞狀態,退出阻塞代碼。如下:

 1 package interrupt;  2 
 3 public class Main {  4     /**
 5  * @param args  6      */
 7     public static void main(String[] args) {  8         Main main = new Main();  9         Thread t = new Thread(main.runnable); 10         System.out.println("mainmainmain"); 11  t.start(); 12         try { 13             Thread.sleep(2000); 14         } catch (InterruptedException e) { 15             // TODO Auto-generated catch block
16  e.printStackTrace(); 17  } 18  t.interrupt(); 19  } 20 
21     Runnable runnable = new Runnable() { 22  @Override 23         public void run() { 24             int i = 0; 25             try { 26                 while (i < 1000) { 27                     Thread.sleep(500); 28                     System.out.println(i++); 29  } 30             } catch (InterruptedException e) { 31  e.printStackTrace(); 32  } 33  } 34  }; 35 }

運行結果:

1 mainmainmain 2 0
3 1
4 2
5 java.lang.InterruptedException: sleep interrupted 6  at java.lang.Thread.sleep(Native Method) 7     at interrupt.Main$1.run(Main.java:27) 8     at java.lang.Thread.run(Thread.java:619)

三:分析下例子中的中斷機制

 1.為什么調用interrupt()並不能中斷線程?

 1 public void interrupt() {  2     if (this != Thread.currentThread())  3  checkAccess();  4 
 5     synchronized (blockerLock) {  6         Interruptible b = blocker;  7         if (b != null) {  8         interrupt0();        // Just to set the interrupt flag
 9  b.interrupt(); 10         return; 11  } 12  } 13  interrupt0(); 14     }

如上是Java源碼中的代碼,由此我們看出問題的答案。線程的blocker字段(也就是interrupt status)默認是null(®1)。調用interrupt()方法時,只是運行了®2,並沒有進入if語句,所以沒調用真正執行中斷的代碼b.interrupt().

2.若調用sleep()而使線程處於阻塞狀態,這時調用interrupt()方法,會拋出InterruptedException,從而使線程提前結束阻塞狀態,退出阻塞代碼。為什么?

由上圖可看出例子中30行代碼鋪好的異常其實是interrupt()拋出的,而不是sleep()拋出的。

歡迎交流!

Author:Andy Zhai

2014-01-12  17:54:39

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM