<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

JDK如何優雅退出一個線程?

馬哥Linux運維 ? 來源:cnblogs ? 2023-11-17 10:02 ? 次閱讀

JDK 在線程的 Stop 方法時明確不得強行銷毀一個線程,要優雅的退出線程。

何謂優雅退出線程,即業務將進行中請求正確被處理,取消待執行請求,執行資源回收,最終Thread Runable run方法 return 結束執行。

首先問為什么要退出一個線程,再提問如何退出一個線程

1需要線程退出的常見場景

任務執行完成,或異常終止,任務認為無需再占用線程。

線程池根據當前任務執行情況,伸縮線程池。當任務執行較少時,退出空閑的線程。

服務或進程在關閉階段,例如滾動發布時,需要退出線程、關閉線程池、關閉進程。

定時任務、周期任務需要終止執行時,需要退出當前線程?;蛘咄顺霎斍叭蝿盏膱绦?。

總之既然能創建一個線程,就會有退出一個線程的能力。也會有退出線程的場景。

關閉一個線程的方式分為兩種類型:通知線程主動關閉和強行關閉銷毀線程。

2優雅關閉 or 強行關閉

c05da122-84e5-11ee-939d-92fbcf53809c.png

實際上強行關閉一個線程,壞處很多,假如要釋放分布式鎖前,突然關閉線程,那么這個分布式鎖就無法釋放。導致后續正常請求加鎖失敗被阻塞,影響用戶提單等。強行關閉一個線程無異于給服務器直接斷電。

3其他語言和 Java 語言退出線程的方式

除了 Java 其他語言如何退出線程呢,實際上每一種實現方式都有。例如 C++ 中可以通過 ExitThread、TerminateThread 強行終止線程執行。linux 既提供了 pthread_exit C 語言系統調用強行關閉線程,也提供了pthread_cancel通知線程關閉等優雅退出方式。

Java 也分別提供優雅和強制兩種退出方式,但是目前 JDK 中明確極不推薦強制中斷線程,在Thread.stop()強制中斷線程的注釋中, JDK 這樣解釋

Thread.stop()這種方法本身就是不安全的,Stop 一個線程會隨之解鎖這個線程所持有的監視器(可以理解為鎖),如果受這些監視器(鎖)保護的臨界對象處在不一致狀態,則其他線程可能會看到這些對象處于不一致狀態,那么將導致未知的行為。對Thread.Stop()的調用應該被簡單的代碼代替,例如 修改一個變量,目標線程定期檢查這個變量,有序從 run 方法 return 出來。如果目標線程在一個條件變量上 wait,則其他線程應該使用 interrupt 方法中斷目標線程。

實際上關閉一個線程強行和通知是兩種理念,即是否應該相信線程任務的開發者優雅的、快速的主動退出線程,而不是被其他線程強制終止。在 Java 中,退出線程的方式只有一種推薦,即優雅退出,并且 JDK 也給了建議,通過修改變量,由目標線程定期檢查狀態?;蛘咄ㄟ^ interrupt 中斷方式通知目標線程。

下面我們探討下如何優雅退出一個線程?

4優雅退出線程

有哪些方式呢?

業務字段標記

業務系統經常遇到終止一個任務的訴求,例如系統中存在定時任務,例如外賣券包在過期后,未使用的金額,自動給用戶退款。假設任務執行中,我需要重新制定任務的入參,需要先終止任務。如何做呢?

大部分任務類代碼都會循環處理,例如掃描全表執行某個業務邏輯。一定存在循環處理的場景,可以在循環入口處判斷任務是否需要終止執行,這樣通過控制這個字段,我們就可以終止任務執行。

具體實施時,可以通過配置中心控制某一個任務是否要終止。

while(config.isTaskEnable()){
//從配置中心獲取任務是否要終止
//循環執行業務邏輯。直到執行完成退出,或者被終止。
}

這種退出方式,是告知線程 “你應該在合適時機退出”, 由線程自己選擇在合適的時機檢查該狀態。那么開發者在設計任務代碼時,就要提前設計 合理的退出點,在退出點檢查是否需要退出。

Thread.interrupt()

JDK 中提到了如果目標線程沒有處于運行態,而是處于阻塞狀態,自然無法檢查退出的狀態標記,如何通知這個線程退出呢?

JDK: 如果目標線程在一個條件變量上 wait,則其他線程應該使用 interrupt 方法中斷目標線程。

interrupt 的 JDK 注釋提到,

如果其他線程調用目標線程的 interrupt 方法,

恰好目標線程在調用。Object.wait(),object.join (),Object.sleep()等方法時,目標線程的中斷位標記被清除,同時目標線程會立即從 sleep、wait 等調用中恢復,并且被拋出InterruptException。

如果目標線程在 IO 操作中被阻塞,例如io.channels.InterruptibleChannel,Channel 將被關閉,線程的中斷位被設置,同時目標線程收到java.nio.channels.ClosedByInterruptException。

如果目標線程被阻塞在java.nio.channels.Selector,線程中斷狀態被設置,然后目標線程立即從 select 中返回非零值。

如果其他條件都不成立,該線程中斷位會被設置。

線程中斷位標記了當前線程是否處于被中斷狀態,并且提供了Thread.isInterrupted方法查看當前是否處于中斷位?那為什么目標線程阻塞在Object.wait(),Sleep()方法時,拋出了interruptException,會取消標記呢?實際上 interrupt 操作執行兩件事,1)設置中斷位標記 2)通過 unpark 喚醒目標線程(park 和 unpark 分別可以阻塞線程和喚醒線程)。

然而目標線程醒來時會檢查當前是否處于中斷位,如果是 sleep 或者 wait 操作。如果處于中斷位則取消中斷位,拋出異常。取消中段位的原因應該是一種規范,即拋出中斷異常,即通知了線程中斷,無需再用中段位標記。

其他場景 2、場景 3 在被喚醒后,分別執行對應的中斷響應策略。

interrupt 中斷邏輯是確定的,業務線程要考慮自己是否調用了 sleep、wait 或者 io、selector 等操作,根據不同的場景,選擇自己合適的中斷響應策略。

那么推薦業務線程如何響應中斷呢?

推薦的中斷響應策略

立即響應中斷

目標線程的任務在InterruptedException異常處理中,要主動回收資源,打印日志,退出任務執行。

目標線程如果沒有阻塞操作,例如 sleep、wait??梢酝ㄟ^Thread.isInterrupted(),查看當前中斷位狀態,如果被中斷了,則采取以上第一步操作。

忽略中斷,交給上一層處理

所謂上一層,可以理解為是調用堆棧的上一層,例如本層代碼不負責處理中斷這個場景,那么 Interrupt 異常被拋出后,可以選擇如何方案:

拋出InterruptedException給上層,由上層代碼處理。

調用Thread.interrupt()。重新設置中斷位標記 (自己中斷自己)。由上游代碼在本層方法返回后,檢查中斷位標記,進行中斷處理。

當然最推薦的方式還是拋出InterruptedException,讓上游感知到下游調用鏈中存在阻塞,讓上游對中斷異常進行處理。

千萬不要吞掉中斷

什么是吞掉中斷?例如當 sleep 拋出InterruptedException后,忽略異常,不執行任何操作,繼續執行業務邏輯。

for(inti=0;i

如果這樣處理,中斷異常被忽略,中斷標記位也被忽略。即便上游方法對中斷有處理策略,也無法感知到中斷。例如上游調用可能會判斷。

while(true){

callChildMethod();//調用下游方法,但是下游吞掉了中斷
if(Thread.currentThread().isInterrupted()){
//回收資源,退出線程
}
}

有人會問,既然上層都能知道處理中斷,為什么下層方法開發者會不記得拋出中斷或重置中斷位呢?

因為上下兩層,很可能不是一個開發者。例如上層是通用的框架代碼,定義了任務的指定邏輯,提供了擴展點方法,下游只需要實現擴展方法即可。但是另一個開發者在實現擴展點方法時,吞掉了中斷異常,導致本來框架層已經處理好中斷了,但還是無法響應中斷。

所以中斷的響應是需要上下層,每一層代碼邏輯都需要考慮的事情。就算框架層處理好中斷異常處理,業務邏輯層也要關注中斷處理。

最后提醒一下,Thread.interrupted方法會返回當前中斷標記,并且取消中斷位。如果只查詢中斷位,不想清理,可以使用Thread.isInterrupted()。

5總結

不推薦強制銷毀線程,會導致資源無法被釋放,進行中請求無法正常處理完,導致業務數據處于不可知的狀態。

Java 推薦優雅退出線程。

業務層可以使用字段標記,定期檢查是否需要退出任務。

Thread.interrupt中斷目標線程、isInterrupted查詢中斷位標記。

使用Thread.interrupt處理中斷也可以優雅退出,但需要上下層堆棧都要關注中斷,不得吞掉中斷。

編輯:黃飛

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2905

    瀏覽量

    103097
  • C語言
    +關注

    關注

    180

    文章

    7538

    瀏覽量

    130340
  • C++
    C++
    +關注

    關注

    21

    文章

    2066

    瀏覽量

    72934
  • JDK
    JDK
    +關注

    關注

    0

    文章

    77

    瀏覽量

    16497
  • 線程
    +關注

    關注

    0

    文章

    494

    瀏覽量

    19516

原文標題:JDK 推薦的線程關閉方式

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    【RT-Thread學習筆記】如何優雅退出QEMU模擬器?

    【RT-Thread學習筆記】如何優雅退出QEMU模擬器?我想重新編譯源碼,再次運行新的代碼,怎么辦呢?如何才能退出這個QEMU命令行控制臺?
    的頭像 發表于 07-26 04:06 ?4839次閱讀
    【RT-Thread學習筆記】如何<b class='flag-5'>優雅</b>地<b class='flag-5'>退出</b>QEMU模擬器?

    RTThread中main線程循環,如果main線程異常退出了,有什么辦法可以監測到?

    RTThread中main線程循環,如果main線程異常退出了,有什么辦法可以監測到?
    發表于 02-22 08:15

    Linux多線程線程間同步

    線程將執行的函數的參數。嵌入式系統學習意義氣嗚嗚吧久零就易,如果想傳遞多個參數,請將它們封裝在結構體中。pthread_join:用于等待某個線程
    發表于 12-08 14:14

    線程退出機制初探

    ,使得前兩循環也結束了。從而達到按“退出”鍵的目的。為了加深理解,可以做另一個測試。將其中循環的內部延時加長,如20秒。點擊“
    發表于 02-13 20:13

    JDK 15安裝步驟及新特性

    Shenandoah 垃圾回收算法轉正  Shenandoah 垃圾回收從實驗特性變為產品特性。這是JDK 12 引入的回收算法,該算法通過與正在運行的 Java 線程同時進
    發表于 12-23 17:36

    rt_thread_create()創建的線程,運行完自己退出,操作系統會回收該線程的棧資源嗎?

    我用下面這個代碼,不停的創建線程,線程執行完自己的任務就會退出,但是我每次都在運行到第64次的時候,執行create返回RT_NULL,這是什么原因?rt_thread_create()創建的
    發表于 03-29 11:25

    Linux線程實現與線程控制步驟簡析

    退出線程的方法是使用函數 pthread_exit。由于 exit 的作用是使調用進程終止,往往進程包含多個線程,因此,在使用 exit 之后,該進程中的所有
    發表于 04-25 09:29

    LWIP相關的線程是如何通過信號控制創建的呢

    。但該功能的實現依賴LWIP創建的幾個線程:etx/erx/tcip/modbustcptest,如果優雅穩定可靠地的通過開關信號(按鍵,菜單啟停)做到這點?
    發表于 07-13 10:21

    教你種如何優雅退出QEMU模擬器的方法

    1、如何優雅退出QEMU模擬器大家都知道,Linux退出控制臺啟動的程序,使用CTRL+C就可以把它
    發表于 08-26 16:12

    線程編程之Linux線程編程

    的可移植性。 (1)函數說明。 創建線程實際上就是確定調用該線程函數的入口點,這里通常使用的函數是pthread_create()。在線程創建以后,就開始運行相關的線程函數,在該函數運
    發表于 10-18 15:55 ?3次下載

    什么是JVM?淺談JRE、JDK和JVM的關系

    當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用Runtime類或者System.exit()來退出。
    發表于 04-25 11:51 ?5126次閱讀
    什么是JVM?淺談JRE、<b class='flag-5'>JDK</b>和JVM的關系

    JDK 19 / Java 19正式發布 虛擬線程來了

    記錄模式 (預覽版) Linux/RISC-V 移植 外部函數和內存 API (預覽版) 虛擬線程(預覽版) Vector API (第四次孵化) Switch 模式匹配(第三預覽版) 結構化并發(孵化階段) JDK 19 / J
    的頭像 發表于 10-10 17:08 ?1348次閱讀

    面試官:線程池中多余的線程是如何回收的?

    最近閱讀了JDK線程池ThreadPoolExecutor的源碼,對線程池執行任務的流程有了大體了解,實際上這個流程也十分通俗易懂,就不再贅述了,別人寫的比我好多了。
    的頭像 發表于 11-07 10:46 ?518次閱讀

    細數線程池的10個坑

    JDK開發者提供了線程池的實現類,我們基于Executors組件,就可以快速創建一個線程池 。
    的頭像 發表于 06-16 10:11 ?490次閱讀
    細數<b class='flag-5'>線程</b>池的10個坑

    JDK 21 GA,虛擬線程正式穩定!你還堅守Java8?

    Java 21 / JDK 21 已正式 GA,此版本是繼 JDK 17 后的長期支持版本 (LTS),Oracle 將為其提供至少八年的技術支持和更新。
    的頭像 發表于 09-20 15:47 ?679次閱讀
    <b class='flag-5'>JDK</b> 21 GA,虛擬<b class='flag-5'>線程</b>正式穩定!你還堅守Java8?
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>