以前一直以為線程的"中斷"就是讓線程停止.
如果你也這麼認為,那你對多線程編程還沒有入門.
在java中,線程的中斷(interrupt)只是改變了線程的中斷狀態,至於這個中斷狀態改變後
帶來的結果,那是無法確定的,有時它更是讓停止中的線程繼續執行的唯一手段.不但不是
讓線程停止運行,反而是繼續執行線程的手段.
對於執行一般邏輯的線程,如果調用調用它的interrupt()方法,那麼對這個線程沒有任何
影響,比如線程a正在執行:
while(條件) x ++;
這樣的語句,如果其它線程調用a.interrupt();那麼並不會影響a對象上啟動並執行線程,如果
在其它線程裡測試a的中斷狀態它已經改變,但並不會停止這個線程的運行.
在一個線程對象上調用interrupt()方法,真正有影響的是wait,join,sleep方法,當然這三個
方法包括它們的重載方法.
請注意:[上面這三個方法都會拋出InterruptedException],
一個線程在調用interrupt()後,自己不會拋出InterruptedException異常,所以你看到
interrupt()並沒有拋出這個異常,所以我上面說如果線程a正在執行while(條件) x ++;
你調用a.interrupt();後線程會繼續正常地執行下去.
但是,如果一個線程被調用了interrupt()後,它的狀態是已中止的.這個狀態對於正在執行
wait,join,sleep的線程,卻改變了線程的運行結果.
(1)sleep方法與interrupt方法
假如線程A像下面這樣,使用線程的sleep()方法暫停著.
- Thread.sleep(10000);
如果要取消他的等待狀態,可以在正在執行的線程裡(比如這裡是B)調用
- a.interrupt(); //a是線程A的執行個體
當sleep中的線程被調用interrupt()時,就會放棄暫停狀態.並拋出InterruptedException.丟出異常的,是A線程.
執行interrupt()時,並不需要擷取Thread執行個體的鎖定.任何線程在任何時刻,都可以調用其他線程interrupt().
順便加個與Thread.sleep()相同效果的代碼:
public static void amethod(long x) throws InterruptedExcetion{
if (x != 0) {
Object o = new Object();
synchronized (o) {
o.wait(x);
}
}
}
(2) wait方法與interrupt方法
對於wait中等待notify/notifyAll喚醒的線程,其實這個線程已經"暫停"執行,因為
它正在某一對象的休息室中,這時如果它的中斷狀態被改變,那麼它就會拋出
InterruptedException異常.
但是這個InterruptedException異常不是線程拋出的,而是wait方法,也就是對象的wait方法內部
會不斷檢查在此對象上休息的線程的狀態,如果發現哪個線程的狀態被置為已中止,則會拋出
InterruptedException,意思就是這個線程不能再等待了,其意義就等同於喚醒它了.
線程B執行下面的語句後,與sleep時一樣,線程A會拋出InterruptedException異常.
- a.interrupt();
不過這時候要小心鎖定的問題.線程在進入等待區,會把鎖定解除,當對等待中的線程調用interrupt()時(注意是等待的線程調用其自己的interrupt()),會先重新擷取鎖定,再拋出異常.在擷取鎖定之前,是無法拋出異常的.
這裡唯一的區別是,被notify/All喚醒的線程會繼續執行wait下面的語句,而在wait
中被中斷的線程則將控制權交給了catch語句.一些正常的邏輯要被放到catch中來運行.
但有時這是唯一手段,比如一個線程a在某一對象b的wait中等待喚醒,其它線程必須
擷取到對象b的監視鎖才能調用b.notify()[All],否則你就無法喚醒線程a,但在任何線程中可
以無條件地調用a.interrupt();來達到這個目的.只是喚醒後的邏輯你要放在catch中,當然同
notify/All一樣,繼續執行a線程的條件還是要等拿到b對象的監視鎖.
(3)Join方法與interrupt方法
當線程以join()等待其他線程結束時,一樣可以使用interrupt()取消之.因為調用join()不需要擷取鎖定,故與sleep()時一樣,會馬上跳到catch塊裡.
注意是誰調用interrupt()方法,一定是阻塞的線程來調用其自己的interrupt方法.如線上程a中調用來線程t.join().則a會等t執行完後在執行t.join後的代碼,a會阻塞。當線上程b中調用來a.interrupt()方法,線程A會拋出InterruptedException異常.
(4)interrupt方法只是改變中斷狀態而已
interrupt()不會中斷一個正在啟動並執行線程。這一方法實際上完成的是,線上程受到阻塞時拋出一個中斷訊號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那麼,interrupt()後,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。
如果線程沒有被阻塞,這時調用interrupt()將不起作用;否則,線程就將得到異常(該線程必須事先預備好處理此狀況),接著逃離阻塞狀態。
線程A在執行sleep,wait,join時,線程B調用A的interrupt方法,的確這一個時候A會有InterruptedException異常拋出來.但這其實是在sleep,wait,join這些方法內部會不斷檢查中斷狀態的值,而自己拋出的InterruptedException。
如果線程A正在執行一些指定的操作時如賦值,for,while,if,調用方法等,都不會去檢查中斷狀態,所以線程A不會拋出InterruptedException,而會一直執行著自己的操作.當線程A終於執行到wait(),sleep(),join()時,才馬上會拋出InterruptedException.
若沒有調用sleep(),wait(),join()這些方法,或是沒有線上程裡自己檢查中斷狀態自己拋出InterruptedException的話,那InterruptedException是不會被拋出來的.
對於一般介紹多線程模式的書上,他們會這樣來介紹:當一個線程被中斷後,在進入
wait,sleep,join方法時會拋出異常.
System.out.println("now interrupt");<br />Thread.currentThread().interrupt();<br />System.out.println("now sleep");<br />Thread.sleep(1000);
now interrupt<br />now sleep<br />Exception in thread "main" java.lang.InterruptedException: sleep interrupted<br />at java.lang.Thread.sleep(Native Method)<br />at nameNode.Namenode.main(Namenode.java:273)
是的,這一點也沒有錯,但是這有什麼意義呢?如果你知道那個線程的狀態已經處於中
斷狀態,為什麼還要讓它進入這三個方法呢?當然有時是必須這麼做的,但大多數時候沒有這麼
做的理由,所以我上面主要介紹了在已經調用這三個方法的線程上調用interrupt()方法讓它中
這本個方法的"暫停"狀態中恢複過來.這個恢複過來就可以包含兩個目的:
一.[可以使線程繼續執行],那就是在catch語句中招待醒來後的邏輯,或由catch語句
轉回正常的邏輯.總之它是從wait,sleep,join的暫停狀態活過來了.
二.[可以直接停止線程的運行],當然在catch中什麼也不處理,或return,那麼就完成
了當前線程的使命,可以使在上面"暫停"的狀態中立即真正的"停止".