java線程中斷?interrupt?和?LockSupport解析
本文大綱
本文章將要介紹的內(nèi)容有以下幾點(diǎn),讀者朋友也可先自行思考一下相關(guān)問(wèn)題:
- 線程中斷 interrupt 方法怎么理解,意思就是線程中斷了嗎?那當(dāng)前線程還能繼續(xù)執(zhí)行嗎?
- 判斷線程是否中斷的方法有幾個(gè),它們之間有什么區(qū)別?
- LockSupport的 park/unpark 和 wait/notify 有什么區(qū)別?
- sleep 方法是怎么響應(yīng)中斷的?
- park 方法又是怎么響應(yīng)中斷的?
線程中斷相關(guān)方法
線程中和中斷相關(guān)的方法有三個(gè),分別介紹如下:
1) interrupt
我們一般都說(shuō)這個(gè)方法是用來(lái)中斷線程的,那么這個(gè)中斷應(yīng)該怎么理解呢? 就是說(shuō)把當(dāng)前正在執(zhí)行的線程中斷掉,不讓它繼續(xù)往下執(zhí)行嗎?
其實(shí),不然。 此處,說(shuō)的中斷僅僅是給線程設(shè)置一個(gè)中斷的標(biāo)識(shí)(設(shè)置為true),線程還是會(huì)繼續(xù)往下執(zhí)行的。而線程怎么停止,則需要由我們自己去處理。 一會(huì)兒會(huì)用代碼來(lái)說(shuō)明這個(gè)。
2) isInterrupted
判斷當(dāng)前線程的中斷狀態(tài),即判斷線程的中斷標(biāo)識(shí)是true還是false。 注意,這個(gè)方法不會(huì)對(duì)線程原本的中斷狀態(tài)產(chǎn)生任何影響。
3) interrupted
也是判斷線程的中斷狀態(tài)的。但是,需要注意的是,這個(gè)方法和 isInterrupted 有很大的不同。我們看下它們的源碼:
public boolean isInterrupted() {
return isInterrupted(false);
}
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
//調(diào)用同一個(gè)方法,只是傳參不同
private native boolean isInterrupted(boolean ClearInterrupted);首先 isInterrupted 方法是線程對(duì)象的方法,而 interrupted 是Thread類(lèi)的靜態(tài)方法。

其次,它們都調(diào)用了同一個(gè)本地方法 isInterrupted,不同的只是傳參的值,這個(gè)參數(shù)代表的是,是否要把線程的中斷狀態(tài)清除(清除即不論之前的中斷狀態(tài)是什么值,最終都會(huì)設(shè)置為false)。
因此,interrupted 靜態(tài)方法會(huì)把原本線程的中斷狀態(tài)清除,而 isInterrupted 則不會(huì)。所以,如果你調(diào)用兩次 interrupted 方法,第二次就一定會(huì)返回false,除非中間又被中斷了一次。
下面證明一下 interrupt 方法只是設(shè)置一個(gè)中斷狀態(tài),而不是使當(dāng)前線程中斷運(yùn)行:
public class TestFlag {
static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("線程中斷標(biāo)志:"+Thread.currentThread().isInterrupted());
while (flag){
}
System.out.println("標(biāo)志flag為:" + flag);
System.out.println("線程中斷標(biāo)志:"+Thread.currentThread().isInterrupted());
System.out.println("我還在繼續(xù)執(zhí)行");
}
});
t.start();
Thread.sleep(100);
flag = false;
t.interrupt();
}
}運(yùn)行結(jié)果:
線程中斷標(biāo)志:false
標(biāo)志flag為:false
線程中斷標(biāo)志:true
我還在繼續(xù)執(zhí)行
當(dāng)線程啟動(dòng),還沒(méi)調(diào)用中斷方法時(shí),中斷狀態(tài)為false,然后調(diào)用中斷方法,并把flag設(shè)置為false。此時(shí),run方法跳出while死循環(huán)。我們會(huì)發(fā)現(xiàn)線程的中斷狀態(tài)為true,但是線程還是會(huì)繼續(xù)往下執(zhí)行,直到執(zhí)行結(jié)束。
sleep 響應(yīng)中斷
線程中常用的阻塞方法,如sleep,join和wait 都會(huì)響應(yīng)中斷,然后拋出一個(gè)中斷異常 InterruptedException。但是,注意此時(shí),線程的中斷狀態(tài)會(huì)被清除。所以,當(dāng)我們捕獲到中斷異常之后,應(yīng)該保留中斷信息,以便讓上層代碼知道當(dāng)前線程中斷了。通常有兩種方法可以做到。
一種是,捕獲異常之后,再重新拋出異常,讓上層代碼知道。另一種是,在捕獲異常時(shí),通過(guò) interrupt 方法把中斷狀態(tài)重新設(shè)置為true。
下面,就以sleep方法為例,捕獲中斷異常,然后重新設(shè)置中斷狀態(tài):
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
private int count = 0;
@Override
public void run() {
try {
count = new Random().nextInt(1000);
count = count * count;
System.out.println("count:"+count);
Thread.sleep(5000);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+"線程第一次中斷標(biāo)志:"+Thread.currentThread().isInterrupted());
//重新把線程中斷狀態(tài)設(shè)置為true,以便上層代碼判斷
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName()+"線程第二次中斷標(biāo)志:"+Thread.currentThread().isInterrupted());
}
}
});
t.start();
Thread.sleep(100);
t.interrupt();
}
}結(jié)果:
count:208849
Thread-0線程第一次中斷標(biāo)志:false
Thread-0線程第二次中斷標(biāo)志:true
LockSupport方法介紹
LockSupport 方法中重要的兩個(gè)方法就是park 和 unpark 。
park和interrupt中斷
park方法可以阻塞當(dāng)前線程,如果調(diào)用unpark方法或者中斷當(dāng)前線程,則會(huì)從park方法中返回。
park方法對(duì)中斷方法的響應(yīng)和 sleep 有一些不太一樣。它不會(huì)拋出中斷異常,而是從park方法直接返回,不影響線程的繼續(xù)執(zhí)行。我們看下代碼:
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new ParkThread());
t.start();
Thread.sleep(100); //①
System.out.println(Thread.currentThread().getName()+"開(kāi)始喚醒阻塞線程");
t.interrupt();
System.out.println(Thread.currentThread().getName()+"結(jié)束喚醒");
}
}
class ParkThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"開(kāi)始阻塞");
LockSupport.park();
System.out.println(Thread.currentThread().getName()+"第一次結(jié)束阻塞");
LockSupport.park();
System.out.println("第二次結(jié)束阻塞");
}
}打印結(jié)果如下:
Thread-0開(kāi)始阻塞
main開(kāi)始喚醒阻塞線程
main結(jié)束喚醒
Thread-0第一次結(jié)束阻塞
第二次結(jié)束阻塞
當(dāng)調(diào)用interrupt方法時(shí),會(huì)把中斷狀態(tài)設(shè)置為true,然后park方法會(huì)去判斷中斷狀態(tài),如果為true,就直接返回,然后往下繼續(xù)執(zhí)行,并不會(huì)拋出異常。注意,這里并不會(huì)清除中斷標(biāo)志。
unpark
unpark會(huì)喚醒被park的指定線程。但是,這里要說(shuō)明的是,unpark 并不是簡(jiǎn)單的直接去喚醒被park的線程??聪翵DK的解釋?zhuān)?/p>

unpark只是給當(dāng)前線程設(shè)置一個(gè)許可證。如果當(dāng)前線程已經(jīng)被阻塞了(即調(diào)用了park),則會(huì)轉(zhuǎn)為不阻塞的狀態(tài)。如若不然,下次調(diào)用park方法的時(shí)候也會(huì)保證不阻塞。這句話(huà)的意思,其實(shí)是指,park和unpark的調(diào)用順序無(wú)所謂,只要unpark設(shè)置了這個(gè)許可證,park方法就可以在任意時(shí)刻消費(fèi)許可證,從而不會(huì)阻塞方法。
還需要注意的是,許可證最多只有一個(gè),也就是說(shuō),就算unpark方法調(diào)用多次,也不會(huì)增加許可證。 我們可以通過(guò)代碼驗(yàn)證,只需要把上邊代碼修改一行即可:
//LockSupportTest類(lèi) //原代碼 t.interrupt(); //修改為 LockSupport.unpark(t); LockSupport.unpark(t);
就會(huì)發(fā)現(xiàn),只有第一次阻塞會(huì)被喚醒,但是第二次依然會(huì)繼續(xù)阻塞。結(jié)果如下:
Thread-0開(kāi)始阻塞
main開(kāi)始喚醒阻塞線程
main結(jié)束喚醒
Thread-0第一次結(jié)束阻塞
第二次結(jié)束阻塞
另外,在此基礎(chǔ)上,把主線程的sleep方法去掉(代碼中①處),讓主線程先運(yùn)行,也就是有可能先調(diào)用unpark方法,然后子線程才開(kāi)始調(diào)用park方法阻塞。我們會(huì)發(fā)現(xiàn),出現(xiàn)以下結(jié)果,證明了上邊我說(shuō)的park方法和unpark不分先后順序,park方法可以隨時(shí)消費(fèi)許可證。
main開(kāi)始喚醒阻塞線程
main結(jié)束喚醒
Thread-0開(kāi)始阻塞
Thread-0第一次結(jié)束阻塞
park/unpark和 wait/notify區(qū)別
了解了 park/unpark的用法之后,想必你也能分析出來(lái)它們和 wait、notify有什么不同之處了。
- wait和notify方法必須和同步鎖 synchronized一塊兒使用。而park/unpark使用就比較靈活了,沒(méi)有這個(gè)限制,可以在任何地方使用。
- park/unpark 使用時(shí)沒(méi)有先后順序,都可以使線程不阻塞(前面代碼已驗(yàn)證)。而wait必須在notify前先使用,如果先notify,再wait,則線程會(huì)一直等待。
- notify只能隨機(jī)釋放一個(gè)線程,并不能指定某個(gè)特定線程,notifyAll是釋放鎖對(duì)象中的所有線程。而unpark方法可以喚醒指定的線程。
- 調(diào)用wait方法會(huì)使當(dāng)前線程釋放鎖資源,但使用的前提是必須已經(jīng)獲得了鎖。 而park不會(huì)釋放鎖資源。(以下代碼驗(yàn)證)
public class LockSyncTest {
private static Object lock = new Object();
//保存調(diào)用park的線程,以便后續(xù)喚醒
private static Thread parkedThread;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
synchronized (lock){
System.out.println("unpark前");
LockSupport.unpark(parkedThread);
System.out.println("unpark后");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//和t1線程用同一把鎖時(shí),park不會(huì)釋放鎖資源,若換成this鎖,則會(huì)釋放鎖
synchronized (lock){
System.out.println("park前");
parkedThread = Thread.currentThread();
LockSupport.park();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("park后");
}
}
});
t2.start();
Thread.sleep(100);
t1.start();
}
}
//打印結(jié)果
//park前以上代碼,會(huì)一直卡在t2線程,因?yàn)閜ark不會(huì)釋放鎖,因此t1也無(wú)法執(zhí)行。
如果把t2的鎖換成this鎖,即只要和t1不是同一把鎖,則t1就會(huì)正常執(zhí)行,然后把t2線程喚醒。打印結(jié)果如下:
park前
unpark前
unpark后
park后
以上就是java線程中斷 interrupt 和 LockSupport的詳細(xì)內(nèi)容,更多關(guān)于java 線程中斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java如何通過(guò)Maven管理項(xiàng)目依賴(lài)
這篇文章主要介紹了Java如何通過(guò)Maven管理項(xiàng)目依賴(lài),幫助大家更好的理解和使用maven,感興趣的朋友可以了解下2020-10-10
使用String轉(zhuǎn)換到Map結(jié)構(gòu)
這篇文章主要介紹了使用String轉(zhuǎn)換到Map結(jié)構(gòu),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
springboot使用hibernate validation對(duì)參數(shù)校驗(yàn)的實(shí)現(xiàn)方法
這篇文章主要介紹了spring-boot 使用hibernate validation對(duì)參數(shù)進(jìn)行優(yōu)雅的校驗(yàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
Java實(shí)現(xiàn)簡(jiǎn)單通訊錄管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
web.xml詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章給大家詳細(xì)介紹了web.xml的相關(guān)知識(shí),需要的朋友可以參考下2017-07-07
Java 實(shí)現(xiàn)鏈表結(jié)點(diǎn)插入
這篇文章主要介紹了Java 實(shí)現(xiàn)鏈表結(jié)點(diǎn)插入操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
JavaWeb建立簡(jiǎn)單三層項(xiàng)目步驟圖解
這篇文章主要介紹了JavaWeb建立簡(jiǎn)單三層項(xiàng)目步驟圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
java并發(fā)容器ConcurrentHashMap深入分析
這篇文章主要為大家介紹了java并發(fā)容器ConcurrentHashMap使用示例及深入分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05

