<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          原來,這才是 JDK 推薦的線程關(guān)閉方式

          共 8005字,需瀏覽 17分鐘

           ·

          2023-11-06 18:35

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          JDK 在線程的 Stop 方法時(shí)明確不得強(qiáng)行銷毀一個(gè)線程,要優(yōu)雅的退出線程。


          何謂優(yōu)雅退出線程,即業(yè)務(wù)將進(jìn)行中請求正確被處理,取消待執(zhí)行請求,執(zhí)行資源回收,最終 Thread Runable run 方法 return 結(jié)束執(zhí)行。

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

          需要線程退出的常見場景

          • 任務(wù)執(zhí)行完成,或異常終止,任務(wù)認(rèn)為無需再占用線程。
          • 線程池根據(jù)當(dāng)前任務(wù)執(zhí)行情況,伸縮線程池。當(dāng)任務(wù)執(zhí)行較少時(shí),退出空閑的線程。
          • 服務(wù)或進(jìn)程在關(guān)閉階段,例如滾動(dòng)發(fā)布時(shí),需要退出線程、關(guān)閉線程池、關(guān)閉進(jìn)程。
          • 定時(shí)任務(wù)、周期任務(wù)需要終止執(zhí)行時(shí),需要退出當(dāng)前線程。或者退出當(dāng)前任務(wù)的執(zhí)行。

          總之既然能創(chuàng)建一個(gè)線程,就會(huì)有退出一個(gè)線程的能力。也會(huì)有退出線程的場景。

          關(guān)閉一個(gè)線程的方式分為兩種類型:通知線程主動(dòng)關(guān)閉和強(qiáng)行關(guān)閉銷毀線程。

          優(yōu)雅關(guān)閉 or 強(qiáng)行關(guān)閉


          實(shí)際上強(qiáng)行關(guān)閉一個(gè)線程,壞處很多,假如要釋放分布式鎖前,突然關(guān)閉線程,那么這個(gè)分布式鎖就無法釋放。導(dǎo)致后續(xù)正常請求加鎖失敗被阻塞,影響用戶提單等。
          強(qiáng)行關(guān)閉一個(gè)線程無異于給服務(wù)器直接斷電。

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

          除了 Java 其他語言如何退出線程呢,實(shí)際上每一種實(shí)現(xiàn)方式都有。例如 C++ 中可以通過 ExitThread、TerminateThread 強(qiáng)行終止線程執(zhí)行。linux 既提供了 pthread_exit C 語言系統(tǒng)調(diào)用強(qiáng)行關(guān)閉線程,也提供了 pthread_cancel 通知線程關(guān)閉等優(yōu)雅退出方式。

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

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

          實(shí)際上關(guān)閉一個(gè)線程強(qiáng)行和通知是兩種理念,即是否應(yīng)該相信線程任務(wù)的開發(fā)者優(yōu)雅的、快速的主動(dòng)退出線程,而不是被其他線程強(qiáng)制終止。在 Java 中,退出線程的方式只有一種推薦,即優(yōu)雅退出,并且 JDK 也給了建議,通過修改變量,由目標(biāo)線程定期檢查狀態(tài)。或者通過 interrupt 中斷方式通知目標(biāo)線程。

          下面我們探討下如何優(yōu)雅退出一個(gè)線程?

          優(yōu)雅退出線程

          有哪些方式呢?

          業(yè)務(wù)字段標(biāo)記

          業(yè)務(wù)系統(tǒng)經(jīng)常遇到終止一個(gè)任務(wù)的訴求,例如系統(tǒng)中存在定時(shí)任務(wù),例如外賣券包在過期后,未使用的金額,自動(dòng)給用戶退款。假設(shè)任務(wù)執(zhí)行中,我需要重新制定任務(wù)的入?yún)ⅲ枰冉K止任務(wù)。如何做呢?大部分任務(wù)類代碼都會(huì)循環(huán)處理,例如掃描全表執(zhí)行某個(gè)業(yè)務(wù)邏輯。一定存在循環(huán)處理的場景,可以在循環(huán)入口處判斷任務(wù)是否需要終止執(zhí)行,這樣通過控制這個(gè)字段,我們就可以終止任務(wù)執(zhí)行。

          具體實(shí)施時(shí),可以通過配置中心控制某一個(gè)任務(wù)是否要終止。


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


          這種退出方式,是告知線程 “你應(yīng)該在合適時(shí)機(jī)退出”, 由線程自己選擇在合適的時(shí)機(jī)檢查該狀態(tài)。那么開發(fā)者在設(shè)計(jì)任務(wù)代碼時(shí),就要提前設(shè)計(jì) 合理的退出點(diǎn),在退出點(diǎn)檢查是否需要退出。


          Thread.interrupt()


          JDK 中提到了如果目標(biāo)線程沒有處于運(yùn)行態(tài),而是處于阻塞狀態(tài),自然無法檢查退出的狀態(tài)標(biāo)記,如何通知這個(gè)線程退出呢?


          JDK: 如果目標(biāo)線程在一個(gè)條件變量上 wait,則其他線程應(yīng)該使用 interrupt 方法中斷目標(biāo)線程。


          interrupt 的 JDK 注釋提到,


          如果其他線程調(diào)用目標(biāo)線程的 interrupt 方法,


          • 恰好目標(biāo)線程在調(diào)用。Object.wait(),object.join (),Object.sleep() 等方法時(shí),目標(biāo)線程的中斷位標(biāo)記被清除,同時(shí)目標(biāo)線程會(huì)立即從 sleep、wait 等調(diào)用中恢復(fù),并且被拋出 InterruptException。

          • 如果目標(biāo)線程在 IO 操作中被阻塞,例如 io.channels.InterruptibleChannel,Channel 將被關(guān)閉,線程的中斷位被設(shè)置,同時(shí)目標(biāo)線程收到 java.nio.channels.ClosedByInterruptException。

          • 如果目標(biāo)線程被阻塞在 java.nio.channels.Selector,線程中斷狀態(tài)被設(shè)置,然后目標(biāo)線程立即從 select 中返回非零值。

          • 如果其他條件都不成立,該線程中斷位會(huì)被設(shè)置。


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


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


          其他場景 2、場景 3 在被喚醒后,分別執(zhí)行對應(yīng)的中斷響應(yīng)策略。


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


          那么推薦業(yè)務(wù)線程如何響應(yīng)中斷呢?


          推薦的中斷響應(yīng)策略


          立即響應(yīng)中斷


          • 目標(biāo)線程的任務(wù)在 InterruptedException 異常處理中,要主動(dòng)回收資源,打印日志,退出任務(wù)執(zhí)行。

          • 目標(biāo)線程如果沒有阻塞操作,例如 sleep、wait。可以通過 Thread.isInterrupted(),查看當(dāng)前中斷位狀態(tài),如果被中斷了,則采取以上第一步操作。


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


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


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

          • 調(diào)用 Thread.interrupt()。重新設(shè)置中斷位標(biāo)記 (自己中斷自己)。由上游代碼在本層方法返回后,檢查中斷位標(biāo)記,進(jìn)行中斷處理。


          當(dāng)然最推薦的方式還是拋出 InterruptedException,讓上游感知到下游調(diào)用鏈中存在阻塞,讓上游對中斷異常進(jìn)行處理。


          千萬不要吞掉中斷


          什么是吞掉中斷?例如當(dāng) sleep 拋出 InterruptedException 后,忽略異常,不執(zhí)行任何操作,繼續(xù)執(zhí)行業(yè)務(wù)邏輯。


          for (int i = 0; i < cnt; i++) {   try {      //執(zhí)行業(yè)務(wù)邏輯      Thread.sleep(10000);   } catch (InterruptedException e) {
          System.out.println("被中斷"); } System.out.println("子線程執(zhí)行中");}

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


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

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

          因?yàn)樯舷聝蓪樱芸赡懿皇且粋€(gè)開發(fā)者。例如上層是通用的框架代碼,定義了任務(wù)的指定邏輯,提供了擴(kuò)展點(diǎn)方法,下游只需要實(shí)現(xiàn)擴(kuò)展方法即可。但是另一個(gè)開發(fā)者在實(shí)現(xiàn)擴(kuò)展點(diǎn)方法時(shí),吞掉了中斷異常,導(dǎo)致本來框架層已經(jīng)處理好中斷了,但還是無法響應(yīng)中斷。

          所以中斷的響應(yīng)是需要上下層,每一層代碼邏輯都需要考慮的事情。就算框架層處理好中斷異常處理,業(yè)務(wù)邏輯層也要關(guān)注中斷處理。

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

          總結(jié)

          • 不推薦強(qiáng)制銷毀線程,會(huì)導(dǎo)致資源無法被釋放,進(jìn)行中請求無法正常處理完,導(dǎo)致業(yè)務(wù)數(shù)據(jù)處于不可知的狀態(tài)。
          • Java 推薦優(yōu)雅退出線程。
          • 業(yè)務(wù)層可以使用字段標(biāo)記,定期檢查是否需要退出任務(wù)。
          • Thread.interrupt 中斷目標(biāo)線程、isInterrupted 查詢中斷位標(biāo)記。
          • 使用 Thread.interrupt 處理中斷也可以優(yōu)雅退出,但需要上下層堆棧都要關(guān)注中斷,不得吞掉中斷。


          ?轉(zhuǎn)自:五陽神功,?

          鏈接:juejin.cn/post/7291564831710445622


              
              

            

                 
                 

                  
                  

          1、華為/榮耀把谷歌當(dāng)病毒殺了

          2、MySQL到底是 join 性能好,還是in一下更快呢?

          3、Next.js支持在前端代碼中寫SQL,開倒車還是遙遙領(lǐng)先?

          4、為什么國外JetBrains做 IDE 就可以養(yǎng)活自己,國內(nèi)不行?區(qū)別在哪?

          5、相比高人氣的Rust、Go,為何 Java、C 在工具層面進(jìn)展緩慢?

          6、互聯(lián)網(wǎng)博物館爆火,網(wǎng)友滿滿回憶殺,看看你能想起幾個(gè)?

          點(diǎn)

          點(diǎn)

          點(diǎn)點(diǎn)

          點(diǎn)在看

          瀏覽 1525
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花视频A片无码资源 | 无码一区二区黑人猛烈视频网站 | 中国第一毛片 | av无码av天天av天天啊 北条麻妃 无码 在线 视频 | 色中色亚洲导航 |