什么是線程中斷
Java中斷機(jī)制是一種協(xié)作機(jī)制,也就是說通過中斷并不能直接終止另一個(gè)線程糊昙,而需要被中斷的線程自己處理中斷.
當(dāng)一個(gè)線程運(yùn)行時(shí),另一個(gè)線程可以調(diào)用對應(yīng)的 Thread 對象的 interrupt()方法來中斷它,該方法只是在目標(biāo)線程中設(shè)置一個(gè)標(biāo)志,表示它已經(jīng)被中斷,并立即返回焚廊。這里需要注意的是咆瘟,如果只是單純的調(diào)用 interrupt()方法,線程并沒有實(shí)際被中斷,會(huì)繼續(xù)往下執(zhí)行。
線程自身也可以中斷自身佛嬉。
感性認(rèn)識(shí)中斷
示例代碼
/**
* Created by haicheng.lhc on 07/07/2017.
*
* @author haicheng.lhc
* @date 2017/07/07
*/
public class TestInterrupt extends Object implements Runnable {
/**
*
*/
public void run() {
try {
System.out.println("in run() - about to sleep for 20 seconds");
Thread.sleep(20000);
System.out.println("in run() - woke up");
} catch (InterruptedException e) {
System.out.println("in run() - interrupted while sleeping");
//處理完中斷異常后湾揽,返回到run()方法人口,
//如果沒有return,線程不會(huì)實(shí)際被中斷民晒,它會(huì)繼續(xù)打印下面的信息
return;
}
System.out.println("in run() - leaving normally");
}
public static void main(String[] args) {
TestInterrupt si = new TestInterrupt();
Thread t = new Thread(si);
t.start();
//主線程休眠2秒刮便,從而確保剛才啟動(dòng)的線程有機(jī)會(huì)執(zhí)行一段時(shí)間
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("in main() - interrupting other thread");
//中斷線程t
t.interrupt();
System.out.println("in main() - leaving");
}
}
結(jié)果是
主線程啟動(dòng)新線程后,自身休眠 2 秒鐘,允許新線程獲得運(yùn)行時(shí)間掂名。新線程打印信息about to sleep for 20 seconds后嗜诀,繼而休眠 20 秒鐘发皿,大約 2 秒鐘后封救,main 線程通知新線程中斷券躁,那么新線程的 20 秒的休眠將被打斷,從而拋出 InterruptException 異常也拜,執(zhí)行跳轉(zhuǎn)到 catch 塊以舒,打印出interrupted while sleeping信息,并立即從 run()方法返回慢哈,然后消亡蔓钟,而不會(huì)打印出 catch 塊后面的leaving normally信息。
另外卵贱,如果將 catch 塊中的 return 語句注釋掉滥沫,則線程在拋出異常后,會(huì)繼續(xù)往下執(zhí)行键俱,而不會(huì)被中斷兰绣,從而會(huì)打印出leaving normally信息。
待決中斷
在上面的例子中编振,sleep()方法的實(shí)現(xiàn)檢查到休眠線程被中斷缀辩,它會(huì)相當(dāng)友好地終止線程,并拋出 InterruptedException 異常。另外一種情況臀玄,如果線程在調(diào)用 sleep()方法前被中斷瓢阴,那么該中斷稱為待決中斷,它會(huì)在剛調(diào)用 sleep()方法時(shí)健无,立即拋出 InterruptedException 異常炫掐。
/**
* Created by haicheng.lhc on 07/07/2017.
*
* @author haicheng.lhc
* @date 2017/07/07
*/
public class PendingInterrupt extends Object {
public static void main(String[] args) {
//如果輸入了參數(shù),則在mian線程中中斷當(dāng)前線程(亦即main線程)
if (args.length > 0) {
Thread.currentThread().interrupt();
}
//獲取當(dāng)前時(shí)間
long startTime = System.currentTimeMillis();
try {
Thread.sleep(2000);
System.out.println("was NOT interrupted");
} catch (InterruptedException x) {
System.out.println("was interrupted");
}
//計(jì)算中間代碼執(zhí)行的時(shí)間
System.out.println("elapsedTime=" + (System.currentTimeMillis() - startTime));
}
}
結(jié)果
中斷的原理
Java中斷機(jī)制是一種協(xié)作機(jī)制睬涧,也就是說通過中斷并不能直接終止另一個(gè)線程募胃,而需要被中斷的線程自己處理中斷
Java中斷模型也是這么簡單,每個(gè)線程對象里都有一個(gè)boolean類型的標(biāo)識(shí)(不一定就要是Thread類的字段畦浓,實(shí)際上也的確不是痹束,這幾個(gè)方法最終都是通過native方法來完成的),代表著是否有中斷請求(該請求可以來自所有線程讶请,包括被中斷的線程本身)祷嘶。例如,當(dāng)線程t1想中斷線程t2夺溢,只需要在線程t1中將線程t2對象的中斷標(biāo)識(shí)置為true论巍,然后線程2可以選擇在合適的時(shí)候處理該中斷請求,甚至可以不理會(huì)該請求风响,就像這個(gè)線程沒有被中斷一樣嘉汰。
- java.lang.Thread類提供了幾個(gè)方法來操作這個(gè)中斷狀態(tài),這些方法包括:
方法名 | 描述 |
---|---|
public static boolean interrupted | 測試當(dāng)前線程是否已經(jīng)中斷状勤。線程的中斷狀態(tài) 由該方法清除鞋怀。換句話說,如果連續(xù)兩次調(diào)用該方法持搜,則第二次調(diào)用將返回 false(在第一次調(diào)用已清除了其中斷狀態(tài)之后密似,且第二次調(diào)用檢驗(yàn)完中斷狀態(tài)前,當(dāng)前線程再次中斷的情況除外)葫盼。 |
public boolean isInterrupted() | 測試線程是否已經(jīng)中斷残腌。線程的中斷狀態(tài)不受該方法的影響。 |
public void interrupt() | 中斷線程贫导。 |
- interrupt方法是唯一能將中斷狀態(tài)設(shè)置為true的方法抛猫。
- 靜態(tài)方法interrupted會(huì)將當(dāng)前線程的中斷狀態(tài)清除,但這個(gè)方法的命名極不直觀脱盲,很容易造成誤解
中斷的處理
既然Java中斷機(jī)制只是設(shè)置被中斷線程的中斷狀態(tài)邑滨,那么被中斷線程該做些什么日缨?
1钱反、處理時(shí)機(jī)
顯然,作為一種協(xié)作機(jī)制,不會(huì)強(qiáng)求被中斷線程一定要在某個(gè)點(diǎn)進(jìn)行處理面哥。實(shí)際上哎壳,被中斷線程只需在合適的時(shí)候處理即可,如果沒有合適的時(shí)間點(diǎn)尚卫,甚至可以不處理归榕,這時(shí)候在任務(wù)處理層面,就跟沒有調(diào)用中斷方法一樣吱涉∩残梗“合適的時(shí)候”與線程正在處理的業(yè)務(wù)邏輯緊密相關(guān),例如怎爵,每次迭代的時(shí)候特石,進(jìn)入一個(gè)可能阻塞且無法中斷的方法之前等,但多半不會(huì)出現(xiàn)在某個(gè)臨界區(qū)更新另一個(gè)對象狀態(tài)的時(shí)候鳖链,因?yàn)檫@可能會(huì)導(dǎo)致對象處于不一致狀態(tài)姆蘸。
處理時(shí)機(jī)決定著程序的效率與中斷響應(yīng)的靈敏性。頻繁的檢查中斷狀態(tài)可能會(huì)使程序執(zhí)行效率下降芙委,相反逞敷,檢查的較少可能使中斷請求得不到及時(shí)響應(yīng)。如果發(fā)出中斷請求之后灌侣,被中斷的線程繼續(xù)執(zhí)行一段時(shí)間不會(huì)給系統(tǒng)帶來災(zāi)難推捐,那么就可以將中斷處理放到方便檢查中斷,同時(shí)又能從一定程度上保證響應(yīng)靈敏度的地方侧啼。當(dāng)程序的性能指標(biāo)比較關(guān)鍵時(shí)玖姑,可能需要建立一個(gè)測試模型來分析最佳的中斷檢測點(diǎn),以平衡性能和響應(yīng)靈敏性慨菱。
2焰络、處理方式
(1)中斷狀態(tài)的管理
一般說來,當(dāng)可能阻塞的方法聲明中有拋出InterruptedException則暗示該方法是可中斷的符喝,如BlockingQueue#put闪彼、BlockingQueue#take、Object#wait协饲、Thread#sleep等畏腕,如果程序捕獲到這些可中斷的阻塞方法拋出的InterruptedException或檢測到中斷后,這些中斷信息該如何處理茉稠?一般有以下兩個(gè)通用原則:
如果遇到的是可中斷的阻塞方法拋出InterruptedException描馅,可以繼續(xù)向方法調(diào)用棧的上層拋出該異常,如果是檢測到中斷而线,則可清除中斷狀態(tài)并拋出InterruptedException铭污,使當(dāng)前方法也成為一個(gè)可中斷的方法恋日。
若有時(shí)候不太方便在方法上拋出InterruptedException,比如要實(shí)現(xiàn)的某個(gè)接口中的方法簽名上沒有throws InterruptedException嘹狞,這時(shí)就可以捕獲可中斷方法的InterruptedException并通過Thread.currentThread.interrupt()來重新設(shè)置中斷狀態(tài)岂膳。如果是檢測并清除了中斷狀態(tài),亦是如此磅网。
一般的代碼中谈截,尤其是作為一個(gè)基礎(chǔ)類庫時(shí),絕不應(yīng)當(dāng)吞掉中斷涧偷,即捕獲到InterruptedException后在catch里什么也不做簸喂,清除中斷狀態(tài)后又不重設(shè)中斷狀態(tài)也不拋出InterruptedException等。因?yàn)橥痰糁袛酄顟B(tài)會(huì)導(dǎo)致方法調(diào)用棧的上層得不到這些信息燎潮。
(2)中斷的響應(yīng)
程序里發(fā)現(xiàn)中斷后該怎么響應(yīng)娘赴?這就得視實(shí)際情況而定了。有些程序可能一檢測到中斷就立馬將線程終止跟啤,有些可能是退出當(dāng)前執(zhí)行的任務(wù)诽表,繼續(xù)執(zhí)行下一個(gè)任務(wù)……作為一種協(xié)作機(jī)制,這要與中斷方協(xié)商好隅肥,當(dāng)調(diào)用interrupt會(huì)發(fā)生些什么都是事先知道的竿奏,如做一些事務(wù)回滾操作,一些清理工作腥放,一些補(bǔ)償操作等泛啸。若不確定調(diào)用某個(gè)線程的interrupt后該線程會(huì)做出什么樣的響應(yīng),那就不應(yīng)當(dāng)中斷該線程秃症。