Java多線程中斷機制三種方法及示例
概述
之前講解Thread類中方法的時候,interrupt()、interrupted()、isInterrupted()三個方法沒有講得很清楚,只是提了一下。現(xiàn)在把這三個方法同一放到這里來講,因為這三個方法都涉及到多線程的一個知識點----中斷機制。
Java沒有提供一種安全、直接的方法來停止某個線程,而是提供了中斷機制。中斷機制是一種協(xié)作機制,也就是說通過中斷并不能直接終止另一個線程,而需要被中斷的線程自己處理。有個例子舉個蠻好,就像父母叮囑出門在外的子女要注意身體一樣,父母說了,但是子女是否注意身體、如何注意身體,還是要看自己。
中斷機制也是一樣的,每個線程對象里都有一個標識位表示是否有中斷請求(當然JDK的源碼是看不到這個標識位的,是虛擬機線程實現(xiàn)層面的),代表著是否有中斷請求。
三個中斷方法
上面說了,中斷標識位是JDK源碼看不到的,是虛擬機線程實現(xiàn)層面的。下面結(jié)合代碼逐一看一下這三個方法的作用,以及為什么中斷標識位是虛擬機實現(xiàn)層面的:
1、interrupt()
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(); return; } } interrupt0(); }
結(jié)果
/* Some private helper methods */ private native void setPriority0(int newPriority); private native void stop0(Object o); private native void suspend0(); private native void resume0(); private native void interrupt0();
分兩部分看:
(1)第一部分的第8行注釋說得很清楚了,interrupt0()方法的作用是"Just to set the interrupt flag",即方法的作用僅僅是設(shè)置中斷標識位
(2)第二部分的第6行就是interrupt0()方法的原型,由于方法是被native修飾的,很明顯這是一個本地方法,是Java虛擬機實現(xiàn)的
2、isInterrupted()
方法唯一的作用只是測試線程是否已經(jīng)中斷,中斷標識位的狀態(tài)并不受到該方法的影響,看一下Java是如何實現(xiàn)這個方法的:
/** * Tests whether this thread has been interrupted. The <i>interrupted * status</i> of the thread is unaffected by this method. * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if this thread has been interrupted; * <code>false</code> otherwise. * @see #interrupted() * @revised 6.0 */ public boolean isInterrupted() { return isInterrupted(false); }
private native boolean isInterrupted(boolean ClearInterrupted);
注意一下第一部分的第2行和第3行,"The interrupted statis of the thread is unaffected by this method",即線程的中斷狀態(tài)不受到這個方法的影響。最終調(diào)用的是isInterrupted(boolean ClearInterrupted),這個方法是一個native的,看得出也是Java虛擬機實現(xiàn)的。方法的參數(shù)ClearInterrupted,顧名思義,清除中斷標識位,這里傳遞false,明顯就是不清除
3、interrupted()
方法的作用是測試當前線程是否已經(jīng)中斷,線程的中斷標識位由該方法清除。換句話說,連續(xù)兩次調(diào)用該方法的返回值必定是false??匆幌逻@個方法是如何實現(xiàn)的:
/** * Tests whether the current thread has been interrupted. The * <i>interrupted status</i> of the thread is cleared by this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if the current thread has been interrupted; * <code>false</code> otherwise. * @see #isInterrupted() * @revised 6.0 */ public static boolean interrupted() { return currentThread().isInterrupted(true);
private native boolean isInterrupted(boolean ClearInterrupted);
同樣,第2行和第3行的注釋已經(jīng)寫得很清楚了,"Theinterruptedstatusofthethreadisclearedbythismethod",即線程的中斷狀態(tài)由此方法清除。另外,interrupted()方法和isInterrupted()方法調(diào)用的是同一個native方法,無非這個方法傳入的是true,表示清除中斷標識位
此外,JDKAPI中有些類的方法也可能會調(diào)用中斷,比如FutureTask的cancel,如果傳入true則會在正在運行的異步任務(wù)上調(diào)用interrupt()方法,又如ThreadPoolExecutor中的shutdownNow方法會遍歷線程池中的工作線程并調(diào)用線程的interrupt()方法。這些場景下只要代碼沒有對中斷作出響應(yīng),那么任務(wù)將一直執(zhí)行下去。
中斷處理時機
這其實是一個很寬泛的、沒有標注答案的話題。顯然,作為一種協(xié)作機制,不會強求被中斷的線程一定要在某個點進行中斷處理。實際上,被中斷線程只需要在合適的時候處理即可,如果沒有合適的時間點,甚至可以不處理。"合適的時間點"就和業(yè)務(wù)邏輯密切相關(guān)了。
處理時機決定著程序的效率和響應(yīng)的靈敏度。頻繁的檢查中斷可能會導(dǎo)致程序執(zhí)行效率低下,較少的檢查則可能導(dǎo)致中斷請求得不到及時響應(yīng)。在實際場景中,如果性能指標比較關(guān)鍵,可能需要建立一個測試模型來分析最佳的中斷檢測點,以平衡性能和響應(yīng)靈敏性。
線程中斷舉例
寫了這么多理論,寫一個例子來演示一下中斷:
public static void main(String[] args) throws Exception { Runnable runnable = new Runnable() { public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("線程被中斷了"); return ; } else { System.out.println("線程沒有被中斷"); } } } }; Thread t = new Thread(runnable); t.start(); Thread.sleep(3000); t.interrupt(); System.out.println("線程中斷了,程序到這里了"); }
看一下運行結(jié)果:
... 線程沒有被中斷 線程沒有被中斷 線程沒有被中斷 線程沒有被中斷 線程沒有被中斷 線程中斷了,程序到這里了 線程被中斷了
代碼分為以下幾步:
1、main函數(shù)起一個t線程
2、main函數(shù)3秒鐘之后給t線程打一個中斷標識位,表示t線程要中斷
3、t線程無限輪詢自己的中斷標識位,中斷了則打印、退出,否則一直運行
從控制臺上打印的語句看到,3秒鐘中斷后,打印出該打印的語句后,就停止了。那這種場景就是前面說的"頻繁地檢查",導(dǎo)致程序效率低下;那如果不頻繁地檢查呢,比如在while中的else分支中加上Thread.sleep(500),表示500ms即0.5s檢查一次,那這種場景就是前面說的"中斷得不到及時的響應(yīng)"。
其實這個例子中,t線程完全可以不用去管這個中斷標識位的,不去檢查就好了,只管做自己的事情,這說明中斷標識位設(shè)不設(shè)置是別人的事情,處不處理是我自己的事情,沒有強制要求必須處理中斷。
但是,那些會拋出InterruptedException的方法要除外。像sleep、wait、notify、join,這些方法遇到中斷必須有對應(yīng)的措施,可以直接在catch塊中處理,也可以拋給上一層。這些方法之所以會拋出InterruptedException就是由于Java虛擬機在實現(xiàn)這些方法的時候,本身就有某種機制在判斷中斷標識位,如果中斷了,就拋出一個InterruptedException。
總結(jié)
以上就是本文關(guān)于Java多線程中斷機制三種方法及示例的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
相關(guān)文章
詳解Springboot整合ActiveMQ(Queue和Topic兩種模式)
這篇文章主要介紹了詳解Springboot整合ActiveMQ(Queue和Topic兩種模式),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04Java線程池ForkJoinPool(工作竊取算法)的使用
Fork就是把一個大任務(wù)切分為若干個子任務(wù)并行地執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個大任務(wù)的結(jié)果。Fork/Join?框架使用的是工作竊取算法。本文主要介紹了ForkJoinPool的使用,需要的可以參考一下2022-11-11Spring?中?PageHelper?不生效問題及解決方法
這篇文章主要介紹了Spring?中?PageHelper?不生效問題,使用這個插件時要注意版本的問題,不同的版本可能 PageHelper 不會生效,本文結(jié)合示例代碼給大家介紹的非常詳細,需要的朋友可以參考下2022-12-12SpringBoot使用@PathVariable進行數(shù)據(jù)校驗的流程步驟
在SpringBoot項目中,我們經(jīng)常需要從 URL 中獲取參數(shù)并進行相關(guān)的數(shù)據(jù)校驗,而@PathVariable注解就是一種非常方便的方式,可以讓我們在方法參數(shù)中直接獲取URL中的參數(shù),并進行數(shù)據(jù)校驗,本文將介紹如何使用@PathVariable注解進行數(shù)據(jù)校驗2023-06-06SpringCloud?Gateway實現(xiàn)API接口加解密
這篇文章主要為大家介紹了SpringCloud?Gateway如何實現(xiàn)API接口加解密的,文中的示例代碼講解詳細,對我們學習有一定的幫助,需要的可以參考一下2022-06-06Spring boot 連接多數(shù)據(jù)源過程詳解
這篇文章主要介紹了Spring boot 連接多數(shù)據(jù)源過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-08-08JavaWeb如何實現(xiàn)統(tǒng)一查詢接口(jfinal)
這篇文章主要介紹了JavaWeb如何實現(xiàn)統(tǒng)一查詢接口(jfinal),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-06-06