這一篇我們說(shuō)說(shuō)java線程thread的interrupt中斷機(jī)制。
中斷線程
線程的thread.interrupt()方法是中斷線程,將會(huì)設(shè)置該線程的中斷狀態(tài)位,即設(shè)置為true,中斷的結(jié)果線程是死亡、還是等待新的任務(wù)或是繼續(xù)運(yùn)行至下一步,就取決于這個(gè)程序本身。線程會(huì)不時(shí)地檢測(cè)這個(gè)中斷標(biāo)示位,以判斷線程是否應(yīng)該被中斷(中斷標(biāo)示值是否為true)。它并不像stop方法那樣會(huì)中斷一個(gè)正在運(yùn)行的線程。
判斷線程是否被中斷
判斷某個(gè)線程是否已被發(fā)送過(guò)中斷請(qǐng)求,請(qǐng)使用thread.currentthread().isinterrupted()方法(因?yàn)樗鼘⒕€程中斷標(biāo)示位設(shè)置為true后,不會(huì)立刻清除中斷標(biāo)示位,即不會(huì)將中斷標(biāo)設(shè)置為false),而不要使用thread.interrupted()(該方法調(diào)用后會(huì)將中斷標(biāo)示位清除,即重新設(shè)置為false)方法來(lái)判斷,下面是線程在循環(huán)中時(shí)的中斷方式:
1
2
3
|
while (!thread.currentthread().isinterrupted() && more work to do ){ do more work } |
interrupt之中斷狀態(tài)標(biāo)記
interrupt中斷機(jī)制中有如下方法:
- thread.interrupt(),設(shè)置當(dāng)前中斷標(biāo)記為true(類(lèi)似屬性的set方法)
- thread.isinterrupted(),檢測(cè)當(dāng)前的中斷標(biāo)記(類(lèi)似屬性的get方法)
- thread.interrupted(),檢測(cè)當(dāng)前的中斷標(biāo)記,然后重置中斷標(biāo)記為false(類(lèi)似屬性的get方法+set方法)
因此interrupt中斷機(jī)制并不是真正的將當(dāng)前線程中斷,而是一個(gè)中斷標(biāo)記的變化。我們先用例子來(lái)測(cè)試一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
public class interrupttest { //這里用來(lái)打印消耗的時(shí)間 private static long time = 0 ; private static void resettime(){ time = system.currenttimemillis(); } private static void printcontent(string content){ system.out.println(content + " 時(shí)間:" + (system.currenttimemillis() - time)); } public static void main(string[] args) { test1(); } private static void test1(){ thread1 thread1 = new thread1(); thread1.start(); //延時(shí)3秒后interrupt中斷 try { thread.sleep( 3000 ); } catch (interruptedexception e) { e.printstacktrace(); } thread1.interrupt(); printcontent( "執(zhí)行中斷" ); } private static class thread1 extends thread{ @override public void run() { resettime(); int num = 0 ; while ( true ){ if (isinterrupted()){ printcontent( "當(dāng)前線程 isinterrupted" ); break ; } num++; if (num % 100 == 0 ){ printcontent( "num : " + num); } } } } } |
以上代碼是開(kāi)啟一個(gè)thread1線程,在thread1線程的while循環(huán)中不斷對(duì)num加1,每到100的倍數(shù)打印一次(防止打印太快)。然后主線程在sleep了3000毫秒后,調(diào)用thread1線程的interrupt方法。那么我們看看輸出結(jié)果:
intterupt中斷
可以看到,在耗時(shí)3000毫秒左右,也就是主線程sleep之后執(zhí)行thread1.interrupt();后,thread1線程停止了,而thread1線程的停止是因?yàn)閣hile循環(huán)中的isinterrupted方法返回了true,所以break退出了while循環(huán),也就是說(shuō)interrupt和isinterrupted在這里起到的作用就相當(dāng)于setxx和getxx的作用,維護(hù)著一個(gè)boolean變量。
interrupt之中斷異常處理
當(dāng)然interrupt機(jī)制并不僅僅是一個(gè)中斷狀態(tài)位的變化和檢測(cè),它還可以進(jìn)行中斷異常的處理。我們知道thread.sleep()方法需要捕獲中斷異常,那接下來(lái)我們往其中添加一個(gè)sleep延時(shí)試試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
while ( true ){ if (isinterrupted()){ printcontent( "當(dāng)前線程 isinterrupted" ); break ; } num++; //sleep一下 try { thread.sleep( 1 ); } catch (interruptedexception e) { e.printstacktrace(); } if (num % 100 == 0 ){ printcontent( "num : " + num); } } |
我們?cè)倏纯摧敵鼋Y(jié)果:
intterupt中斷
這里我們會(huì)發(fā)現(xiàn),sleep睡眠之后,輸出的num值明顯小了好多(沒(méi)睡眠時(shí)num都達(dá)到10億的大小了,看來(lái)cpu執(zhí)行簡(jiǎn)單運(yùn)算還是非常快的),哈哈,不過(guò)這不是重點(diǎn),重點(diǎn)是是看到輸出了一個(gè)異常,還有就是輸出異常后,isinterrupted輸出返回false,thread1線程又繼續(xù)執(zhí)行下去了,并沒(méi)有退出while循環(huán)。那么這是為什么呢?我們只是加了一個(gè)sleep睡眠而已。
如果thread1線程中有執(zhí)行需要捕獲interruptedexception異常的操作,比如thread的sleep,join方法,object的wait,condition的await等,它是強(qiáng)制需要捕獲interruptedexception異常的,那么當(dāng)thread1.interrupt方法調(diào)用之后,它會(huì)給thread1線程拋出一個(gè)interruptedexception異常,那么在while循環(huán)中,就能捕獲到這個(gè)異常然后這個(gè)異常拋出之后,又會(huì)馬上將線程中斷標(biāo)識(shí)重置為false,因此在下次的while循環(huán)中判斷isinterrupted時(shí),它是false,也就不會(huì)break,然后while循環(huán)會(huì)一直執(zhí)行下去。
因此interrupt()方法會(huì)根據(jù)thread線程中的run方法里是否有必須捕獲interruptedexception異常的代碼,而做出不同操作:
- 如果沒(méi)有必須捕獲interruptedexception異常的代碼(比如thread.sleep()),則isinterrupted()會(huì)返回true,此時(shí)可以在isinterrupted的判斷中處理中斷變化。
- 如果有必須捕獲interruptedexception異常的代碼(比如thread.sleep()),則會(huì)拋出interruptedexception異常,并進(jìn)行捕獲,同時(shí)重置isinterrupted為false,此時(shí)得在異常捕獲中處理中斷變化。
interrupt的應(yīng)用場(chǎng)景
通常interrupt適用于在線程執(zhí)行中的循環(huán)標(biāo)記判斷,例如
1
2
3
|
while (!isinterrupted()){ ... } |
但是如果在本次循環(huán)中出現(xiàn)阻塞了,那么線程就無(wú)法判斷下次的isinterrupted標(biāo)記,那么即便調(diào)用了interrupt()方法也無(wú)法退出循環(huán),也就無(wú)法退出線程。例如
1
2
3
4
5
6
|
while (!isinterrupted()){ ... while ( true ){ //線程卡在這里了,則無(wú)法響應(yīng)interrupte機(jī)制了 } } |
這樣的話,interrupt就沒(méi)轍了,線程會(huì)一直執(zhí)行下去,不會(huì)被中斷停止。
測(cè)試?yán)硬榭?我的github--javatest
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://www.jianshu.com/p/7f1071293a18