線程停止繼續_線程不是你想中斷就能中斷

這是我2021年的第2篇原創文章,原汁原味的技術之路盡在Jerrycodes
  • 為什么不強制停止

  • 如何用 interrupt 停止線程

  • sleep 期間能否感受到中斷

  • 停止線程的方式有幾種

  • 總結

啟動線程需要調用 Thread 類的 start() 方法,并在 run() 方法中定義需要執行的任務。啟動一個線程非常簡單,但如果想要正確停止它就沒那么容易了。

對于實現線程的幾種方式,可見我的上一篇文章

實現線程本質上只有一種方式

為什么不強制停止

對于 Java 而言,最正確的停止線程的方式是使用?interrupt。但 interrupt僅僅起到通知被停止線程的作用。而對于被停止的線程而言,它擁有完全的自主權,它既可以選擇立即停止,也可以選擇一段時間后停止,也可以選擇壓根不停止。

為什么 Java 不提供強制停止線程的能力呢?事實上,Java 希望程序間能夠相互通知、相互協作地管理線程,因為如果不了解對方正在做的工作,貿然強制停止線程就可能會造成一些安全的問題。

比如:線程正在寫入一個文件,這時收到終止信號,它就需要根據自身業務判斷,是選擇立即停止,還是將整個文件寫入成功后停止。如果選擇立即停止就可能造成數據不完整,不管是中斷命令發起者,還是接收者都不希望數據出現問題。

如何用 interrupt 停止線程

while?(!Thread.currentThread().isInterrupted()?
&&?more?work?to?do)?{
????do?more?work
}

我們一旦調用某個線程的 interrupt() 之后,這個線程的中斷標記位就會被設置成 true。每個線程都有這樣的標記位,當線程執行時,應該定期檢查這個標記位,如果標記位被設置成 true,就說明有程序想終止該線程。

回到源碼,可以看到在 while 循環體判斷語句中,首先通過

Thread.currentThread().isInterrupt()?

判斷線程是否被中斷,隨后檢查是否還有工作要做。&& 邏輯表示只有當兩個判斷條件同時滿足的情況下,才會去執行下面的工作。
public?class?StopThread?implements?Runnable?{
?
????@Override
????public?void?run()?{
????????int?count?=?0;
????????while?(!Thread.currentThread().isInterrupted()?&&?count?1000)?{
????????????System.out.println("count?=?"?+?count++);
????????}
????}
?
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????Thread?thread?=?new?Thread(new?StopThread());
????????thread.start();
????????Thread.sleep(5);
????????thread.interrupt();
????}
}

在 StopThread 類的 run() 方法中,首先判斷線程是否被中斷,然后判斷 count 值是否小于 1000。

這個線程的工作內容很簡單,就是打印 0~999 的數字,每打印一個數字 count 值加 1,可以看到,線程會在每次循環開始之前,檢查是否被中斷了。接下來在 main 函數中會啟動該線程,然后休眠 5 毫秒后立刻中斷線程,該線程會檢測到中斷信號,于是在還沒打印完1000個數的時候就會停下來,這種就屬于通過 interrupt 正確停止線程的情況。

sleep 期間能否感受到中斷

先說結論,可以。

public?class?StopDuringSleep?{
?
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????Runnable?runnable?=?()?->?{
????????????int?num?=?0;
????????????try?{
????????????????while?(!Thread.currentThread().isInterrupted()?&&?num?<=?1000)?{
????????????????????System.out.println(num);
????????????????????num++;
????????????????????Thread.sleep(1000000);
????????????????}
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}
????????};
????????Thread?thread?=?new?Thread(runnable);
????????thread.start();
????????Thread.sleep(5);
????????thread.interrupt();
????}
}

運行后的結果你猜怎么著,程序會拋出異常

38d9a4da9749d0984e9436e8a2037680.png
如果 sleep、wait 等可以讓線程進入阻塞的方法使線程休眠了,而處于休眠中的線程被中斷,那么線程是可以感受到中斷信號的,并且會拋出一個 InterruptedException 異常,同時清除中斷信號,將中斷標記位設置成 false。這樣一來就不用擔心長時間休眠中線程感受不到中斷了,因為即便線程還在休眠,仍然能夠響應中斷通知,并拋出異常。

但是這樣只能相應一次中斷信號了,怎么辦?我的業務還沒有完成收尾,怎么辦?

合理利用好 try/catch

我們在實際開發中不能盲目吞掉中斷,如果不在方法簽名中聲明,也不在 catch 語句塊中再次恢復中斷,而是在 catch 中不作處理,我們稱這種行為是“屏蔽了中斷請求”。如果我們盲目地屏蔽了中斷請求,會導致中斷信號被完全忽略,最終導致線程無法正確停止。

????try?{
????????Thread.sleep(2000);
????}?catch?(InterruptedException?e)?{
//????????此處處理中斷異常請求,業務收尾
????}

停止線程的方式有幾種

void?shutdown;
boolean?isShutdown;
boolean?isTerminated;
boolean?awaitTermination(long?timeout,?TimeUnit?unit)?throws?InterruptedException;
List?shutdownNow;

下面我們就對這些方法逐一展開。

shutdown()

調用 shutdown() 方法之后線程池并不是立刻就被關閉,因為這時線程池中可能還有很多任務正在被執行,或是任務隊列中有大量正在等待被執行的任務,調用 shutdown() 方法后線程池會在執行完正在執行的任務和隊列中等待的任務后才徹底關閉。但這并不代表 shutdown() 操作是沒有任何效果的,調用 shutdown() 方法后如果還有新的任務被提交,線程池則會根據拒絕策略直接拒絕后續新提交的任務。

isShutdown()

它可以返回 true 或者 false 來判斷線程池是否已經開始了關閉工作,也就是是否執行了 shutdown 或者 shutdownNow 方法。這里需要注意,如果調用 isShutdown() 方法的返回的結果為 true 并不代表線程池此時已經徹底關閉了,這僅僅代表線程池開始了關閉的流程,也就是說,此時可能線程池中依然有線程在執行任務,隊列里也可能有等待被執行的任務。

isTerminated()

這個方法可以檢測線程池是否真正“終結”了,這不僅代表線程池已關閉,同時代表線程池中的所有任務都已經都執行完畢了,因為我們剛才說過,調用 shutdown 方法之后,線程池會繼續執行里面未完成的任務,不僅包括線程正在執行的任務,還包括正在任務隊列中等待的任務。比如此時已經調用了 shutdown 方法,但是有一個線程依然在執行任務,那么此時調用 isShutdown 方法返回的是 true ,而調用 isTerminated 方法返回的便是 false ,因為線程池中還有任務正在在被執行,線程池并沒有真正“終結”。直到所有任務都執行完畢了,調用 isTerminated() 方法才會返回 true,這表示線程池已關閉并且線程池內部是空的,所有剩余的任務都執行完畢了。

awaitTermination()

第四個方法叫作 awaitTermination(),它本身并不是用來關閉線程池的,而是主要用來判斷線程池狀態的。比如我們給 awaitTermination 方法傳入的參數是 10 秒,那么它就會陷入 10 秒鐘的等待,直到發生以下三種情況之一:

  1. 等待期間(包括進入等待狀態之前)線程池已關閉并且所有已提交的任務(包括正在執行的和隊列中等待的都執行完畢,相當于線程池已經“終結”了,方法便會返回true

  2. 等待超時時間到后,第一種線程池“終結”的情況始終未發生,方法返回 false

  3. 等待期間線程被中斷,方法會拋出 Interruptedexception異常

等待期間(包括進入等待狀態之前)線程池已關閉并且所有已提交的任務(包括正在執行的和隊列中等待的)都執行完畢,相當于線程池已經“終結”了,方法便會返回 true;

等待超時時間到后,第一種線程池“終結”的情況始終未發生,方法返回 false;等待期間線程被中斷,方法會拋出 InterruptedException 異常。

shutdownNow()

最后一個方法是 shutdownNow(),也是 5 種方法里功能最強大的,它與第一種 shutdown 方法不同之處在于名字中多了一個單詞 Now,也就是表示立刻關閉的意思。在執行 shutdownNow 方法之后,首先會給所有線程池中的線程發送 interrupt 中斷信號,嘗試中斷這些任務的執行,然后會將任務隊列中正在等待的所有任務轉移到一個 List 中并返回,我們可以根據返回的任務 List 來進行一些補救的操作,例如記錄在案并在后期重試。

public?List?shutdownNow()?{?
????List?tasks;final?ReentrantLock?mainLock?=?this.mainLock;
????mainLock.lock();try?{?
????????checkShutdownAccess();
????????advanceRunState(STOP);
????????interruptWorkers();
????????tasks?=?drainQueue();
????}?finally?{?
????????mainLock.unlock();
????}?
????tryTerminate();return?tasks;
?}

源碼中有一行 interruptWorkers() 代碼,這行代碼會讓每一個已經啟動的線程都中斷,這樣線程就可以在執行任務期間檢測到中斷信號并進行相應的處理,提前結束任務。這里需要注意的是,由于 Java 中不推薦強行停止線程的機制的限制,即便我們調用了 shutdownNow 方法,如果被中斷的線程對于中斷信號不理不睬,那么依然有可能導致任務不會停止。

總結

中斷和關閉線程的方式五花八門,看起來很相似,其實里頭大有門道。處理不好,可是會導致程序崩潰的。

碼到這里,何不來個在看?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534502.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534502.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534502.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

倒序查10條數據_10 | 怎么給字符串字段加索引?

現在&#xff0c;幾乎所有的系統都支持郵箱登錄&#xff0c;如何在郵箱這樣的字段上建立合理的索引&#xff0c;是我們今天要討論的問題。假設&#xff0c;你現在維護一個支持郵箱登錄的系統&#xff0c;用戶表是這么定義的&#xff1a;mysql> create table SUser( ID bigin…

保留小數點后三位_【Meta分析】Stata制作森林圖時,如何保留三位小數?

系統評價/Meta分析指全面收集所有相關研究并逐個進行嚴格評價和分析&#xff0c;再用定性或定量合成的方法對資料進行處理得出綜合結論的研究方法。在指導學員的過程中發現初學者在學習過程中常常會碰到許多共性問題&#xff0c;本公眾號特此開設專欄解答&#xff0c;希望能夠和…

android自動計步_Android計步模塊實例代碼(類似微信運動)

最近在項目中研究計步模塊&#xff0c;每天0點開始記錄當天的步數&#xff0c;類似微信運動。碰到了不少坑今天有時間整理出來給大家看看。做之前在google、baidu、github上搜了個遍沒找到好的&#xff0c;大多數都是需要在后臺存活&#xff0c;需要后臺Service。對于現在的各大…

python井字棋ai_實現AI下井字棋的alpha-beta剪枝算法(python實現)

代碼參考自中國大學mooc上人工智能與信息社會陳斌老師的算法&#xff0c;我在原來的基礎上增加了玩家輸入的異常捕獲 AlphaBeta剪枝算法是對Minimax方法的優化&#xff0c;能夠極大提高搜索樹的效率&#xff0c;如果對這個算法感興趣的可以去參考相關資料。 當正確理解AlphaBet…

Redis小計(2)

目錄 1.exists命令 2.del命令 3.expire/pexpire命令 4.ttl命令 5.redis對于key過期的刪除策略 1.exists命令 exists X1 X2 X3 X4&#xff1a;返回四個key存在的個數。 2.del命令 del X1 X2&#xff1a;刪除key。 3.expire/pexpire命令 給key設置超時時間。 expire key…

unity 彩帶粒子_iOS動畫開發----粒子系統---彩帶效果

參考博文地址:http://my.oschina.net/u/2340880/blog/485095?fromerrbgjLq4Mw一、粒子發射器iOS中的粒子效果有兩部分組成&#xff0c;一部分為發射器&#xff0c;設置例子發射的宏觀屬性&#xff0c;另一部分是粒子單元&#xff0c;用于設置相應的粒子屬性。粒子發射器是基于…

一秒執行一次_《一秒鐘》:一貫的粗曠式抓大放小,張藝謀的自命題作業總是要觀眾自己再做一遍...

還有不變的永遠在奔跑的大棉褲花棉襖的圓臉妮子&#xff0c;這是導演張藝謀最新作品《一秒鐘》的最直接觀感。張藝謀是個善于從普世情懷處挖掘題材的導演。之前諸多現實題材類型作品&#xff0c;諸如講父子和解的《千里走單騎》、夫妻愛情的《歸來》以及《我的父親母親》&#…

latex 作者加小標_Latex 寫期刊論文的小技巧

在不同文字處理系統(如 MiKTeX, TeX Live, CTeX, cwTex) 或 不同整合開發環境 ( 如Texstudio, WinEdt, TeXstudio, TeXmaker) 中&#xff0c;我用了 Miktex Texstudio 的常用組合 (win10環境中)。1: 先MiKTeX&#xff0c;后Texstudio ;2&#xff1a; 安裝包(packages);3&#…

unity 畸變_unity3d 幾種鏡頭畸變

1.Fisheye distortion 魚眼鏡頭解釋來自百度百科&#xff1a;魚眼鏡頭是一種焦距為16mm或更短的并且視角接近或等于180。 它是一種極端的廣角鏡頭&#xff0c;“魚眼鏡頭”是它的俗稱。為使鏡頭達到最大的攝影視角&#xff0c;這種攝影鏡頭的前鏡片直徑很短且呈拋物狀向鏡頭前…

restfull加簽_SpringBoot RestFull API簽名

一、需求如下對指定的API路徑進行簽名認證&#xff0c;對于沒有指定的無需認證&#xff0c;認證具體到方法。二、查閱資料與開發1.了解JWT&#xff0c;實際上用的開源jjwt2.編寫自定義注解3.編寫攔截器&#xff0c;主要是攔截特定的url進行簽名驗證&#xff0c;這里解析請求的h…

mysql 5.5.18下載_MySQL5.7.18下載和安裝過程圖文詳解

MySql下載1、打開官網找到下載路口&#xff0c;這里直接給出下載的地址2、選擇64位版本3、直接下載MySql5.7.18.1安裝過程1 、運行安裝軟件&#xff0c;接受協議2、選擇默認安裝3、下一步到檢查環境界面&#xff0c;點擊“Execute”執行檢查 (可以后面單獨下載插件安裝)&…

mysql找不到performance_Mysql安裝完畢運行時沒有mysql和performance_schema數據庫_MySQL

Mysql問題 ERROR 1045 (28000): Access denied for user ‘root’’localhost’ (using password: YES)Mysql安裝完畢運行時沒有 mysql 和 performance_schema 數據庫問題一&#xff1a;之前卸載未卸載干凈問題二&#xff1a;沒有管理員權限進入問題三&#xff1a;登錄時&#…

mysql latid1_mysql觸發器的實戰經驗

1 引言Mysql的觸發器和存儲過程一樣&#xff0c;都是嵌入到mysql的一段程序。觸發器是mysql5新增的功能&#xff0c;目前線上鳳巢系統、北斗系統以及哥倫布系統使用的數據庫均是mysql5.0.45版本&#xff0c;很多程序比如fc-star管理端&#xff0c;sfrd(das)&#xff0c;dorad…

mysql數據庫sql注入原理_SQL注入原理解析以及舉例1

sql注入是指web應用程序對用戶輸入數據的合法性沒有判斷&#xff0c;導致攻擊者可以構造不同的sql語句來實現對數據庫的操作。sql注入漏洞產生滿足條件&#xff1a;1&#xff1b;用戶能夠控制數據的輸入。2&#xff1b;原本需要執行的代碼&#xff0c;拼接了用戶的輸入。舉例&a…

mysql存儲map數據結構_map數據結構

Go map實現原理 - 戀戀美食的個人空間 - OSCHINA - 中文開源技術交流社區 https://my.oschina.net/renhc/blog/2208417// A header for a Go map.type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.// Make sure this…

四因素三水平正交表_做論文要用正交表?我打包送給你

正交試驗目前在國內的應用量仍然是比較高的&#xff0c;許多高校畢業生喜歡利用正交試驗來獲取研究數據&#xff0c;最終完成畢業論文的撰寫或者期刊投稿。正交試驗方案的設計&#xff0c;必然要用到(標準)正交表。那么大家都是從哪里獲取正交表的呢&#xff1f;小兵給這方面的…

plsql視圖添加表字段_Oracle-單表多字段查詢(不使用*)

環境&#xff1a;Oracle 11g&#xff0c;plsql 14目的&#xff1a;不使用*,查詢擁有上百個字段的表的所有字段。懶人大法&#xff1a;在文章末尾。sql實現邏輯&#xff1a;1、首先建一張100個字段以上的表&#xff0c;通過excel的方式將表建好后直接復制粘貼到plsql的建表界面。…

mysql 編譯安裝與rpm安裝的區別_編譯安裝與RPM安裝的區別

建議在安裝線上的生產服務器軟件包時都用源碼安裝&#xff0c;這是因為源碼安裝可以自行調整編譯參數&#xff0c;最大化地定制安裝結果。這里以MySQL 5線上環境的編譯安裝來說明之&#xff0c;其編譯參數如下所示&#xff1a;./configure-prefix/usr/local/mysql -without-deb…

python字符串變量s的值是python網絡爬蟲_【Python爬蟲作業】-字符串

一、定義字符串變量1.請定義三個字符串a,b,c值分別為 I,like, python2.請將上面三個變量合并輸出I like pythonaIblikecpythonprint(a)print(b)print(c)print(a,b,c)二、定義一個變量 s sdghHhf 1.請先將變量s的空白符去掉 賦值給新變量s1 打印輸出2.請分別將s1變為全部大寫(命…

lableimg閃退_CV學習筆記(二十五):數據集標注與制作

最近在做一些數據標注的工作&#xff0c;雖然標注數據比較枯燥&#xff0c;但這也是每個做算法的工程師升級打怪的必由之路。使用一些合適的工具往往可以事半功倍&#xff0c;效率UP。一&#xff1a;數據標注流程二&#xff1a;數據處理的一些小代碼1&#xff1a;重命名當得到這…