Java中的interrupted()和isInterrupted()
1、前言
當(dāng)提及如何終止一個(gè)線程時(shí),部分讀者通常立馬想到的方法肯定是stop()
,但是stop()
方法并不被推薦使用(很多規(guī)范中是禁止使用的),其原因是強(qiáng)制終止一個(gè)線程,會(huì)導(dǎo)致程序不正常的結(jié)束,會(huì)出現(xiàn)資源未正確釋放、程序結(jié)果不正確等等問題。而是否終止一個(gè)線程應(yīng)該把這個(gè)控制權(quán)轉(zhuǎn)交給當(dāng)前被終止的線程本身,此時(shí)采用的辦法就是 ****interrupt()
方法來終止,該方法相當(dāng)于修改一個(gè)共享變量的值,當(dāng)運(yùn)行中的線程判斷當(dāng)前值為false
則繼續(xù)運(yùn)行,如果有地方調(diào)用當(dāng)前thread
的interrupt()
方法,那么這個(gè)值將變?yōu)?code>true,此時(shí)當(dāng)前線程可以根據(jù)這個(gè)值的修改來正確的終止線程的運(yùn)行。
2、API
在java.lang.Thread
中主要提供了如下與線程中斷相關(guān)的方法,其具體方法名與主要作用如下表所示。
方法名 | 方法作用 |
---|---|
public void?interrupt() | 中斷此線程 |
public static boolean?interrupted() | 測試當(dāng)前線程是否被中斷,該方法會(huì)恢復(fù)(清除)中斷標(biāo)志 |
public boolean?isInterrupted() | 測試當(dāng)前線程是否被中斷,該方法只會(huì)獲取中斷標(biāo)志,不會(huì)恢復(fù)(清除)中斷標(biāo)志 |
private native boolean?isInterrupted(boolean?ClearInterrupted); | interrupted()和isInterrupted()最終調(diào)用,該方法是native本地方法,在jvm中具體實(shí)現(xiàn),也是獲取線程中斷標(biāo)志真正調(diào)用的方法,參數(shù)ClearInterrupted意思是是否恢復(fù)(清除)中斷標(biāo)志 |
源碼:
/** * 中斷此線程 */ public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } /** * 測試當(dāng)前線程是否被中斷,返回中斷標(biāo)志 */ public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * 測試當(dāng)前線程是否被中斷,返回中斷標(biāo)志 */ public boolean isInterrupted() { return isInterrupted(false); } /** * 線程是否被中斷native方法,ClearInterrupted為是否清除中斷標(biāo)志參數(shù) */ private native boolean isInterrupted(boolean ClearInterrupted); /** * 中斷當(dāng)前線程的native方法 */ private native void interrupt0();
3、interrupted()和isInterrupted()區(qū)別
看了上述API講述和Thread
中的源碼,已經(jīng)清楚interrupted()
和isInterrupted()
的主要區(qū)別了
interrupted()
為靜態(tài)方法,isInterrupted()
為普通方法
interrupted()
返回中斷標(biāo)志且清除(恢復(fù))中斷標(biāo)志,isInterrupted()
僅返回中斷標(biāo)志
3.1 使用方法
我們先驗(yàn)證中斷異常響應(yīng),通過如下兩種方法的使用示例來介紹,注意Runner中的run方法的部分區(qū)別
方法一
package com.liziba.p7; import java.util.concurrent.TimeUnit; /** * <p> * * </p> * * @Author: Liziba * @Date: 2021/6/24 21:05 */ public class ThreadInterruptedDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runner(), "Thread-01"); t1.start(); // 主線程睡眠1秒,保證t1的充分執(zhí)行 TimeUnit.SECONDS.sleep(1); // 發(fā)起中斷 t1.interrupt(); } static class Runner implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + " is running ."); } } } }
輸出結(jié)果:
可以看到線程在執(zhí)行數(shù)次后終止運(yùn)行
方法二
package com.liziba.p7; import java.util.concurrent.TimeUnit; /** * <p> * * </p> * * @Author: Liziba * @Date: 2021/6/24 21:18 */ public class ThreadInterruptedDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runner(), "Thread-01"); t1.start(); // 主線程睡眠2秒,保證t1的充分執(zhí)行 TimeUnit.SECONDS.sleep(1); // 發(fā)起中斷 t1.interrupt(); } static class Runner implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + " is running ."); try { // 睡眠2秒,保證主線程發(fā)起的中斷能被捕獲 TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { // 不對中斷做任何處理,try住異常,打印 e.printStackTrace(); } } } } }
輸出結(jié)果:
可以看到main
線程中發(fā)起的t1線程中斷,被捕獲住異常后,未做任何處理,線程繼續(xù)持續(xù)不斷的運(yùn)行
總結(jié)上述兩種方式:
方法一和方法二,均通過判斷Thread.currentThread().isInterrupted()
的值來運(yùn)行run
方法中的邏輯,Thread.currentThread().isInterrupted()
在線程未中斷時(shí)返回false
,當(dāng)main
線程中執(zhí)行 t1.interrupt()
時(shí),線程t1被中斷,Thread.currentThread().isInterrupted()
的值變?yōu)?code>false;在方法一中,獲取到這個(gè)變化后直接結(jié)束運(yùn)行;在方法二中,由于sleep()
使得線程阻塞會(huì)響應(yīng)中斷,但是此時(shí)我僅僅catch
住異常,并沒有對中斷做任何處理,這里有個(gè)知識(shí)點(diǎn)是,線程響應(yīng)中斷拋出異常時(shí),會(huì)恢復(fù)(清除)中斷標(biāo)志,所以t1.interrupt()
對中斷標(biāo)志的修改又被恢復(fù)了,程序仍然不斷的運(yùn)行。
接下來我們來驗(yàn)證interrupted()對于中斷的標(biāo)志的清除
package com.liziba.p7; import java.util.concurrent.TimeUnit; /** * <p> * isInterrupted() * </p> * * @Author: Liziba * @Date: 2021/6/24 21:20 */ public class ThreadInterruptDemo2 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runner(), "Thread-1"); thread.start(); TimeUnit.SECONDS.sleep(2); thread.interrupt(); } static class Runner implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() +" interrupted flag is " + Thread.currentThread().isInterrupted()); while (!Thread.currentThread().isInterrupted()) { try { System.out.println(Thread.currentThread().getName() + " is running ."); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // 響應(yīng)中斷,拋出異常后中斷位置會(huì)被復(fù)位,自己中斷自己 Thread.currentThread().interrupt(); // 這里調(diào)用isInterrupted()獲取當(dāng)前的中斷標(biāo)志 System.out.println(Thread.currentThread().getName() +" interrupted flag is " + Thread.currentThread().isInterrupted()); } } } } }
輸出結(jié)果:
這里證明interrupted()
不清楚中斷標(biāo)志,線程在獲取到 thread.interrupt()發(fā)
起中斷后,執(zhí)行結(jié)束。
將上述catch
中的Thread.currentThread().isInterrupted()
修改為Thread.interrupted()
再次運(yùn)行
package com.liziba.p7; import java.util.concurrent.TimeUnit; /** * <p> * * </p> * * @Author: Liziba * @Date: 2021/6/24 21:23 */ public class ThreadInterruptDemo2 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runner(), "Thread-1"); thread.start(); TimeUnit.SECONDS.sleep(2); thread.interrupt(); } // 區(qū)別在catch中 static class Runner implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() +" interrupted flag is " + Thread.currentThread().isInterrupted()); while (!Thread.currentThread().isInterrupted()) { try { System.out.println(Thread.currentThread().getName() + " is running ."); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // 響應(yīng)中斷,拋出異常后中斷位置會(huì)被復(fù)位,自己中斷自己 Thread.currentThread().interrupt(); // 注意區(qū)別在這里 System.out.println(Thread.currentThread().getName() +" interrupted flag is " + Thread.interrupted()); } } } } }
輸出結(jié)果:
線程也響應(yīng)到了 thread.interrupt()
的中斷,但是由于catch
中調(diào)用了Thread.interrupted()
,對中斷標(biāo)志進(jìn)行了清除,所以!Thread.currentThread().isInterrupted()
判斷仍然等于true
,線程繼續(xù)不斷的運(yùn)行
看到這里,應(yīng)該已經(jīng)理解了這兩個(gè)方法的主要區(qū)別和其使用,最后我們來看下一個(gè)源碼中的使用案例。我們通過觀看AbstractQueuedSynchronizer(AQS)
中的await()
方法,來看其在源碼中的使用。
public final void await() throws InterruptedException { // 判斷當(dāng)前線程是否被中斷,如果被中斷則恢復(fù)中斷標(biāo)志 if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
AbstractQueuedSynchronizer(AQS)
源碼中使用靜態(tài)Thread.interrupted()
,判斷當(dāng)前線程是否被中斷,并恢復(fù)中斷標(biāo)志,如果線程已被中斷則拋出InterruptedException
中斷異常。清除標(biāo)志位的作用就是為了當(dāng)前線程響應(yīng)過中斷后,再次進(jìn)入的時(shí)候可以進(jìn)行后續(xù)操作。
到此這篇關(guān)于Java
中的interrupted()
和isInterrupted()
的文章就介紹到這了,更多相關(guān)interrupted()和isInterrupted()內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java詳解實(shí)現(xiàn)ATM機(jī)模擬系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)控制臺(tái)版本的ATM銀行管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Spring MVC+FastJson+Swagger集成的完整實(shí)例教程
這篇文章主要給大家分享介紹了關(guān)于Spring MVC+FastJson+Swagger集成的完整實(shí)例教程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04java中pdf轉(zhuǎn)圖片的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猨ava中pdf轉(zhuǎn)圖片的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12springboot與數(shù)據(jù)庫返回?cái)?shù)據(jù)中文亂碼
大家好,本篇文章主要講的是springboot與數(shù)據(jù)庫返回?cái)?shù)據(jù)中文亂碼,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01JDK源碼之線程并發(fā)協(xié)調(diào)神器CountDownLatch和CyclicBarrier詳解
我一直認(rèn)為程序是對于現(xiàn)實(shí)世界的邏輯描述,而在現(xiàn)實(shí)世界中很多事情都需要各方協(xié)調(diào)合作才能完成,就好比完成一個(gè)平臺(tái)的交付不可能只靠一個(gè)人,而需要研發(fā)、測試、產(chǎn)品以及項(xiàng)目經(jīng)理等不同角色人員進(jìn)行通力合作才能完成最終的交付2022-02-02spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存的實(shí)例代碼
這篇文章主要介紹了spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存,需要的朋友可以參考下2018-09-09BeanUtils.copyProperties復(fù)制對象結(jié)果為空的原因分析
這篇文章主要介紹了BeanUtils.copyProperties復(fù)制對象結(jié)果為空的原因分析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Spring Boot 2和Redis例子實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring Boot2發(fā)布與調(diào)用REST服務(wù)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11