http://www.cnblogs.com/biGpython/archive/2012/03/05/2380858.htmlhtml
http://blog.csdn.net/hudashi/article/details/6958550java
http://hainiubl.com/topics/29#爲什麼調用wait方法有可能拋出InterruptedException異常python
《Java併發編程的藝術》是這麼寫的:編程
從Java的API中能夠看出,許多聲明拋出InterruptedException的方法(例如Thread.sleep(long millis)方法)這些方法在拋出InterruptedException以前,Java虛擬機會先將該線程的中斷標識位清除(即設置爲false),而後拋出InterruptedException,此時調用isInterrupted()方法將會返回false。併發
這是什麼意思呢?ide
如下面的 InterruptJoinTest 類代碼爲例說明(詳細代碼見InterruptJoinTest 類代碼)。main線程先啓動了t2線程,而後調用t2.interrupt()方法對t2線程進行中斷,this
t2.start();spa
t2.interrupt();.net
回顧下JDK中的源碼以下線程
注意紅框中的註釋,調用t2.interrupt()方法就是將t2的中斷標識位進行了置位(設置爲true)。
注意這裏,調用t2.interrupt()方法對t2線程進行中斷也就是對t2的中斷標識位進行了置位,置位後,t2的中斷標識位爲true,而後t2線程會拋出InterruptedException異常。再回過頭來看《Java併發編程的藝術》中的話:
在拋出InterruptedException以前,Java虛擬機會先將該線程的中斷標識位清除(即設置爲false),而後拋出InterruptedException
也就是說:調用t2.interrupt()方法將中斷標識位設置爲true;而後t2線程要拋出InterruptedException異常,t2拋出InterruptedException異常以前Java虛擬機又會將t2的中斷標識位設置爲false(即將t2的中斷標識位清除)。那麼意味着線程t2的中斷標識位被設置爲true的時間其實至關短暫,立刻隨着InterruptedException異常的拋出又被重置爲了false。
以上這些說明,是爲了更好的理解下面的代碼示例。
研究InterruptedException類代碼的執行結果,會有更深的體會。
public class InterruptJoinTest { public static void main(String... args) { T1 t1 = new T1(); T2 t2 = new T2(t1); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); t2.interrupt(); } } class T1 extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "======" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class T2 extends Thread { private T1 t; public T2(T1 t) { this.t = t; } public void run() { try { t.join(); System.out.println(Thread.currentThread().getName() + "######"); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println(Thread.currentThread().getName() + "$$$$$$$$$$$$$$$" + Thread.currentThread().isInterrupted()); } } }
Thread-0======0
Thread-0======1
Thread-0======2
Thread-0======3
Thread-0======4
Thread-1######
Thread-0======0
Thread-1$$$$$$$$$$$$$$$false
Thread-0======1
Thread-0======2
Thread-0======3
Thread-0======4
t2線程中調用了t1.join(),因此t2線程進入了對象t1的等待隊列(即對象t1的wait set),t2線程要等待t1線程執行完畢才能繼續。此時t2線程在不斷地檢查自身中斷狀態的值(底層來實現)。
主線程即main線程中調用了t2.interrupt(),將t2線程的中斷狀態位設置爲true。同時t2線程不斷地檢查自身中斷狀態的值,發現了置位(中斷狀態位被設置爲true),因此拋出InterruptedException異常,進行了復位(設置爲false),而後t2線程進入了catch異常處理代碼塊。
t1線程沒受什麼影響,繼續本身的運行。
import java.util.concurrent.TimeUnit; public class InterruptWaitTest { volatile static boolean b = true; public static void main(String... strings) throws InterruptedException{ final byte[] lock = new byte[0]; Thread t = new Thread(new Runnable() { public void run() { while (b) { } synchronized (lock) { try { System.out.println(Thread.currentThread().getName() + " in waiting!!"); lock.wait(); System.out.println("@$%^^$^&*#E%^&*("); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println(Thread.currentThread().getName() + " end waiting!!"); System.out.println(Thread.currentThread().isInterrupted()); } } } }, "t"); t.interrupt(); TimeUnit.SECONDS.sleep(1); System.out.println("########## " + t.isInterrupted()); t.start(); // t.interrupt(); // System.out.println("@@@@@@@@@@@@@ " + t.isInterrupted()); // b = false; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
########## false
線程t還沒運行,主線程即main線程中就調用了t.interrupt()方法,此時打印線程t的中斷狀態位,發現線程t的中斷狀態位並未被置位。因此線程t沒有受interrupt()方法的任何影響。啓動線程t後直接執行t裏面的run()方法。
import java.util.concurrent.TimeUnit; public class InterruptWaitTest { volatile static boolean b = true; public static void main(String... strings) throws InterruptedException{ final byte[] lock = new byte[0]; Thread t = new Thread(new Runnable() { public void run() { while (b) { } synchronized (lock) { try { System.out.println(Thread.currentThread().getName() + " in waiting!!"); lock.wait(); System.out.println("@$%^^$^&*#E%^&*("); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println(Thread.currentThread().getName() + " end waiting!!"); System.out.println(Thread.currentThread().isInterrupted()); } } } }, "t"); // t.interrupt(); // TimeUnit.SECONDS.sleep(1); // System.out.println("########## " + t.isInterrupted()); t.start(); t.interrupt(); System.out.println("@@@@@@@@@@@@@ " + t.isInterrupted()); b = false; TimeUnit.SECONDS.sleep(1); } }
@@@@@@@@@@@@@ true
t in waiting!!
t end waiting!!
false
線程t在進入對象lock的等待隊列以前,先在主線程即main線程中被調用interrupt()方法。t.interrupt()方法調用完,線程t的中斷標識位已經被設置爲true。
由於b爲true,線程t在執行while空方法,不會去檢查中斷狀態。
b變爲false,線程t終於執行到lock.wait(),線程t進入了lock的等待隊列即wait set。線程t不斷循環檢查本身的中斷標識位,發現中斷標識位已經爲true,立刻會拋出 InterruptedException,進入catch異常處理代碼塊。
線程t若是在啓動前調用t.interrupt()方法是不起做用的。
線程t啓動後,調用t.interrupt()方法,在線程t進入某個對象的wait set後線程t會不斷檢查本身的中斷標識位狀態,若發現中斷標識位狀態已被設置爲true,線程t就會拋出InterruptedException異常。
注意,只有線程t進入某個對象的wait set,線程t纔會開始不斷循環檢查自身的中斷標識位狀態。不然即便線程t的中斷標識位被設置爲true,線程t不檢查中斷標識位,也不會拋出InterruptedException異常。
import java.util.concurrent.TimeUnit; public class InterruptSleepTest { public static void main(String[] args) throws Exception { // sleepThread不停的嘗試睡眠 Thread sleepThread = new Thread(new SleepRunner(), "SleepThread"); sleepThread.setDaemon(true); // busyThread不停的運行 Thread busyThread = new Thread(new BusyRunner(), "BusyThread"); busyThread.setDaemon(true); sleepThread.start(); busyThread.start(); // 休眠5秒,讓sleepThread和busyThread充分運行 TimeUnit.SECONDS.sleep(5); sleepThread.interrupt(); busyThread.interrupt(); System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted()); System.out.println("BusyThread interrupted is " + busyThread.isInterrupted()); // 防止sleepThread和busyThread馬上退出 TimeUnit.SECONDS.sleep(2); } static class SleepRunner implements Runnable { @Override public void run() { while (true) { // SleepUtils.second(10); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("!@#$%^&*(!@#$%^&*(!@#$%^&*"); } } } } static class BusyRunner implements Runnable { @Override public void run() { while (true) { } } } }
!@#$%^&*(!@#$%^&*(!@#$%^&*
SleepThread interrupted is false
BusyThread interrupted is true
sleepThread.interrupt();
SleepRunner進行中斷,由於SleepRunner正在進行sleep,方法內部會不斷檢查中斷狀態的值,檢測到中斷狀態已經被置位(設置爲true),那麼就會拋出InterruptedException異常,而且將中斷狀態復位。因此看到的結果是跳進了catch異常處理代碼塊,且由於被複位,因此中斷狀態位是false。
busyThread.interrupt();
BusyRunner進行中斷,由於BusyRunner沒有sleep、wait、join,不會去檢查中斷狀態,因此線程A不會拋出 InterruptedException,而會一直執行着本身的操做。這樣interrupt()置位一直沒有清除,因此中斷狀態位仍是true。
程序執行時,最後停頓2秒後終止,爲何BusyRunner對應的線程沒有一直執行呢?不是while(true)嗎?由於 BusyRunner 是Daemon線程。main線程執行完畢後,JVM中就沒有非Daemon線程了,因此JVM就退出了。線程 BusyRunner 就跟着一塊兒結束了。
線程t處於如下三種狀況下,
線程t由於調用某對象的wait()方法進入某對象的wait set;
線程t由於調用其餘線程的join()進入其餘線程(對象)的wait set;
線程t由於調用t.sleep()自身進入超時等待狀態。
實際上這三種狀況下線程t都處於wait set。
線程t在這三種狀態下會不斷循環檢查自身的中斷標識位,此時若是有線程調用t.interrupt()方法,會將線程t的中斷標識位設置爲true,那麼線程t將接收到一箇中斷異常(InterruptedException),而後線程t的中斷標識位會被複位爲false,再而後線程t會進入catch異常處理代碼塊。
通過以上步驟,線程將從wait set中跳出。