<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>

          聊聊貫穿Java并發(fā)編程的中斷機(jī)制

          共 5017字,需瀏覽 11分鐘

           ·

          2020-05-24 23:21

          本來(lái)是要介紹 AQS 作為我們走進(jìn)并發(fā)編程源碼環(huán)節(jié)的第一步,但 AQS 涉及的知識(shí)點(diǎn)也還真有點(diǎn)多,每一個(gè)都?jí)騿为?dú)拿出來(lái)說(shuō)一說(shuō),恰巧有朋友私信我“不理解線程的中斷機(jī)制”,中斷機(jī)制又恰巧是 AQS API實(shí)現(xiàn)的一部分,更貫穿于整個(gè)并發(fā)編程內(nèi)容中。于是就打算單獨(dú)說(shuō)一說(shuō)這個(gè)小機(jī)制,先讓大家做到心中有 number

          在學(xué)習(xí)/編寫(xiě)并發(fā)程序時(shí),總會(huì)聽(tīng)到/看到如下詞匯:

          • 線程被中斷或拋出InterruptedException
          • 設(shè)置了中斷標(biāo)識(shí)
          • 清空了中斷標(biāo)識(shí)
          • 判斷線程是否被中斷

          在 Java Thread 類(lèi)又提供了長(zhǎng)相酷似,讓人傻傻分不清的三個(gè)方法來(lái)處理并發(fā)中斷問(wèn)題:

          • interrupt()
          • interrupted()
          • isInterrupted()

          e6e70ace02edf9a07d319a2c6b09262d.webp

          看到這我不禁會(huì)問(wèn)自己:

          3dc174ef6eabaca06649a2572af59598.webp

          什么是中斷機(jī)制?

          ea899ac82f59669098d91a0d91297d6e.webp

          剛剛接觸【中斷】這個(gè)詞時(shí),先入為主的概念就是“直接中斷/打斷”正在做的事,使其停止。我的理解是這樣的:

          你:在打游戲

          女朋友:別打游戲了,趕快過(guò)來(lái)吃飯

          你:聽(tīng)到女朋友招呼之后立馬中斷手中的游戲乖乖過(guò)去吃飯

          4d6c3ba6f9e395a5a20b48f2214e3754.webp

          在多線程編程中,中斷是一種【協(xié)同】機(jī)制,怎么理解這么高大上的詞呢?就是女朋友叫你吃飯,你收到了中斷游戲通知,但是否馬上放下手中的游戲去吃飯看你心情 。在程序中怎樣演繹這個(gè)心情就看具體的業(yè)務(wù)邏輯了,Java 的中斷機(jī)制就是這么簡(jiǎn)單

          如果還沒(méi)改變這個(gè)先入為主的概念,我懷你沒(méi)有女朋友(?)我們擁抱一下

          為什么會(huì)有中斷機(jī)制?

          中斷是一種協(xié)同機(jī)制,我覺(jué)得就是解決【當(dāng)局者迷】的狀況

          現(xiàn)實(shí)中,你努力忘我沒(méi)有晝夜的工作,如果再?zèng)]有人告知你中斷,你身體是吃不消的。

          在多線程的場(chǎng)景中,有的線程可能迷失在怪圈無(wú)法自拔(自旋浪費(fèi)資源),這時(shí)就可以用其他線程在恰當(dāng)的時(shí)機(jī)給它個(gè)中斷通知,被“中斷”的線程可以選擇在恰當(dāng)的時(shí)機(jī)選擇跳出怪圈,最大化的利用資源

          那程序中如何中斷?怎樣識(shí)別是否中斷?又如何處理中斷呢?這就與上文提到的三個(gè)方法有關(guān)了

          interrupt() VS isInterrupted() VS interrupted()

          Java 的每個(gè)線程對(duì)象里都有一個(gè) boolean 類(lèi)型的標(biāo)識(shí),代表是否有中斷請(qǐng)求,可你尋遍 Thread 類(lèi)你也不會(huì)找到這個(gè)標(biāo)識(shí),因?yàn)檫@是通過(guò)底層 native 方法實(shí)現(xiàn)的。

          interrupt()

          interrupt() 方法是 唯一一個(gè) 可以將上面提到中斷標(biāo)志設(shè)置為 true 的方法,從這里可以看出,這是一個(gè) Thread 類(lèi) public 的對(duì)象方法,所以可以推斷出任何線程對(duì)象都可以調(diào)用該方法,進(jìn)一步說(shuō)明就是可以一個(gè)線程 interrupt 其他線程,也可以 interrupt 自己。其中,中斷標(biāo)識(shí)的設(shè)置是通過(guò) native 方法 interrupt0 完成的

          c90468794c6d4c4b3b59b4847496c420.webp

          在 Java 中,線程被中斷的反應(yīng)是不一樣的,脾氣不好的直接就拋出了 InterruptedException()

          caf4de340fc528e50da48bd80ae5f8a5.webp

          該方法注釋上寫(xiě)的很清楚,當(dāng)線程被阻塞在:

          1. wait()
          2. join()
          3. sleep()

          這些方法時(shí),如果被中斷,就會(huì)拋出 InterruptedException 受檢異常(也就是必須要求我們 catch 進(jìn)行處理的)

          熟悉 JUC 的朋友可能知道,其實(shí)被中斷拋出 InterruptedException 的遠(yuǎn)遠(yuǎn)不止這幾個(gè)方法,比如:

          2ad07ec9afb711282dba15585c46c391.webp

          反向推理,這些可能阻塞的方法如果聲明有 throws InterruptedException , 也就暗示我們它們是可中斷的

          調(diào)用 interrput() 方法后,中斷標(biāo)識(shí)就被設(shè)置為 true 了,那我們?cè)趺蠢眠@個(gè)中斷標(biāo)識(shí),來(lái)判斷某個(gè)線程中斷標(biāo)識(shí)到底什么狀態(tài)呢?

          isInterrupted()

          3f8b61e6f77df81bd8029515df863e7d.webp

          這個(gè)方法名起的非常好,因?yàn)楸容^符合我們 bean boolean 類(lèi)型字段的 get 方法規(guī)范,沒(méi)錯(cuò),該方法就是返回中斷標(biāo)識(shí)的結(jié)果:

          • true:線程被中斷,
          • false:線程沒(méi)被中斷或被清空了中斷標(biāo)識(shí)(如何清空我們一會(huì)兒看)

          拿到這個(gè)標(biāo)識(shí)后,線程就可以判斷這個(gè)標(biāo)識(shí)來(lái)執(zhí)行后續(xù)的邏輯了。有起名好的,也有起名不好的,就是下面這個(gè)方法:

          interrupted()

          按照常規(guī)翻譯,過(guò)去時(shí)時(shí)態(tài),這就是“被打斷了/被打斷的”,其實(shí)和上面的 isInterrupted() 方法差不多,兩個(gè)方法都是調(diào)用 private 的 isInterrupted() 方法, 唯一差別就是會(huì)清空中斷標(biāo)識(shí)(這是從方法名中怎么也看不出來(lái)的)

          dd49f5cbfdeb252fdebc9bef26ba243f.webp

          因?yàn)檎{(diào)用該方法,會(huì)返回當(dāng)前中斷標(biāo)識(shí),同時(shí)會(huì)清空中斷標(biāo)識(shí),就有了那一段有點(diǎn)讓人迷惑的方法注釋?zhuān)?/p>

          c9a1cf5a6c8d8f66db520dcd7159a986.webp

          來(lái)段程序你就會(huì)明白上面注釋的意思了:

          Thread.currentThread().isInterrupted(); // true
          Thread.interrupted() // true,返回true后清空了中斷標(biāo)識(shí)將其置為 false
          Thread.currentThread().isInterrupted(); // false
          Thread.interrupted() // false

          這個(gè)方法總覺(jué)得很奇怪,現(xiàn)實(shí)中有什么用呢?

          當(dāng)你可能要被大量中斷并且你想確保只處理一次中斷時(shí),就可以使用這個(gè)方法了

          該方法在 JDK 源碼中應(yīng)用也非常多,比如(后續(xù)文章會(huì)具體分析,這里知道該方法的作用和使用場(chǎng)景就好):

          0b162d344ba164ad95b9c4ebf27f7a73.webp

          相信到這里你已經(jīng)能明確分辨三胞胎都是誰(shuí),并發(fā)揮怎樣的作用了,那么有哪些場(chǎng)景我們可以使用中斷機(jī)制呢?

          中斷機(jī)制的使用場(chǎng)景

          通常,中斷的使用場(chǎng)景有以下幾個(gè)

          • 點(diǎn)擊某個(gè)桌面應(yīng)用中的關(guān)閉按鈕時(shí)(比如你關(guān)閉 IDEA,不保存數(shù)據(jù)直接中斷好嗎?);
          • 某個(gè)操作超過(guò)了一定的執(zhí)行時(shí)間限制需要中止時(shí);
          • 多個(gè)線程做相同的事情,只要一個(gè)線程成功其它線程都可以取消時(shí);
          • 一組線程中的一個(gè)或多個(gè)出現(xiàn)錯(cuò)誤導(dǎo)致整組都無(wú)法繼續(xù)時(shí);

          因?yàn)橹袛嗍且环N協(xié)同機(jī)制,提供了更優(yōu)雅中斷方式,也提供了更多的靈活性,所以當(dāng)遇到如上場(chǎng)景等,我們就可以考慮使用中斷機(jī)制了

          使用中斷機(jī)制有哪些注意事項(xiàng)

          其實(shí)使用中斷機(jī)制無(wú)非就是注意上面說(shuō)的兩項(xiàng)內(nèi)容:

          1. 中斷標(biāo)識(shí)
          2. InterruptedException

          前浪已經(jīng)將其總結(jié)為兩個(gè)通用原則,我們后浪直接站在肩膀上用就可以了,來(lái)看一下這兩個(gè)原則是什么:

          原則-1

          如果遇到的是可中斷的阻塞方法, 并拋出 InterruptedException,可以繼續(xù)向方法調(diào)用棧的上層拋出該異常;如果檢測(cè)到中斷,則可清除中斷狀態(tài)并拋出 InterruptedException,使當(dāng)前方法也成為一個(gè)可中斷的方法

          原則-2

          若有時(shí)候不太方便在方法上拋出 InterruptedException,比如要實(shí)現(xiàn)的某個(gè)接口中的方法簽名上沒(méi)有 throws InterruptedException,這時(shí)就可以捕獲可中斷方法的 InterruptedException 并通過(guò) Thread.currentThread.interrupt() 來(lái)重新設(shè)置中斷狀態(tài)。

          再通過(guò)個(gè)例子來(lái)加深一下理解:

          本意是當(dāng)前線程被中斷之后,退出while(true),  你覺(jué)得代碼有問(wèn)題嗎?(先不要向下看)

          Thread th = Thread.currentThread();
          while(true) {
            if(th.isInterrupted()) {
              break;
            }
            // 省略業(yè)務(wù)代碼
            try {
              Thread.sleep(100);
            }catch (InterruptedException e){
              e.printStackTrace();
            }
          }

          打開(kāi) Thread.sleep 方法:

          3082c0a4aab6670d382eafb0c4b9287b.webp

          sleep 方法拋出 InterruptedException后,中斷標(biāo)識(shí)也被清空置為 false,我們?cè)赾atch 沒(méi)有通過(guò)調(diào)用 th.interrupt() 方法再次將中斷標(biāo)識(shí)置為 true,這就導(dǎo)致無(wú)限循環(huán)了

          這兩個(gè)原則很好理解。總的來(lái)說(shuō),我們應(yīng)該留意 InterruptedException,當(dāng)我們捕獲到該異常時(shí),絕不可以默默的吞掉它,什么也不做,因?yàn)檫@會(huì)導(dǎo)致上層調(diào)用棧什么信息也獲取不到。其實(shí)在編寫(xiě)程序時(shí),捕獲的任何受檢異常我們都不應(yīng)該吞掉

          JDK 中有哪些使用中斷機(jī)制的地方呢?

          中斷機(jī)制貫穿整個(gè)并發(fā)編程中,這里只簡(jiǎn)單列覺(jué)大家經(jīng)常會(huì)使用的,我們可以通過(guò)閱讀JDK源碼來(lái)進(jìn)一步了解中斷機(jī)制以及學(xué)習(xí)如何使用中斷機(jī)制

          ThreadPoolExecutor

          ThreadPoolExecutor 中的 shutdownNow 方法會(huì)遍歷線程池中的工作線程并調(diào)用線程的 interrupt 方法來(lái)中斷線程

          b28a3165d1874a768896f04b60fc5157.webp

          464adb3202ed7ee8774551f898a25f07.webp

          FutureTask

          FutureTask 中的 cancel 方法,如果傳入的參數(shù)為 true,它將會(huì)在正在運(yùn)行異步任務(wù)的線程上調(diào)用 interrupt 方法,如果正在執(zhí)行的異步任務(wù)中的代碼沒(méi)有對(duì)中斷做出響應(yīng),那么 cancel 方法中的參數(shù)將不會(huì)起到什么效果

          8f1e1b2f541582a7004953e77c3c38a7.webp

          總結(jié)

          到這里你應(yīng)該理解Java 并發(fā)編程中斷機(jī)制的含義了,它是一種協(xié)同機(jī)制,和你先入為主的概念完全不一樣。區(qū)分了三個(gè)相近方法,說(shuō)明了使用場(chǎng)景以及使用原則,同時(shí)又給出JDK源碼一些常見(jiàn)案例,相信你已經(jīng)胸中有溝壑了,接下來(lái),跟上節(jié)奏,我們陸續(xù)走進(jìn)源碼吧

          靈魂追問(wèn)

          1. 拋出 InterruptedException 后,中斷標(biāo)識(shí)就一定被清空嗎?
          2. 處在死鎖狀態(tài)的線程是否可以被中斷呢?
          3. 進(jìn)入臨界區(qū)的線程能否被中斷呢?如果不能有什么辦法能響應(yīng)中斷嗎?
          4. 個(gè)人感覺(jué)interrupted這個(gè)方法名稱(chēng)不是特別好,如果你也覺(jué)得不好,讓你設(shè)計(jì)這個(gè)地方,你有什么想法?

          有朋友可能會(huì)問(wèn)文章開(kāi)頭的圖,同時(shí)看一個(gè)類(lèi)的不同部分怎么實(shí)現(xiàn)的?不等您開(kāi)口,我就全盤(pán)招了,其實(shí)就是屏幕分割(在文件上鼠標(biāo)右鍵->選擇水平/垂直分割),這樣在同時(shí)查看某些代碼時(shí)還是很方便的(帶魚(yú)屏垂直分割真是爽翻天),保姆式演示如下(由于公眾號(hào)限制,完整動(dòng)圖查看原文吧):

          e71e18dbd09f01cd58f5d4a0b32b55c7.webp

          參考

          1. Java 并發(fā)編程實(shí)戰(zhàn)
          2. Java并發(fā)編程的藝術(shù)
          3. https://www.infoq.cn/article/java-interrupt-mechanism
          4. https://coderanch.com/t/237332/certification/explain-interrupt-isInterrupted-interrupted-method
          5. https://dzone.com/articles/waiting-for-coroutines



          d54b0a6975e3acdf9be88308d186c5d1.webp文末福利:我總結(jié)了一套 5000 頁(yè)的 Java 學(xué)習(xí)手冊(cè),在知乎已經(jīng)3萬(wàn)贊了!此手冊(cè)內(nèi)容專(zhuān)注 Java技術(shù),包括 JavaWeb,SSM,Linux,Spring Boot,MyBatis,MySQL,Nginx,Git,GitHub,Servlet,IDEA,多線程,集合,JVM,DeBug, Dubbo,Redis,算法,面試題等相關(guān)內(nèi)容。 cd93378d50db09fdcab68a0ac9179dbd.webp

          下載方式

          1. 首先掃描下方二維碼

          2. 后臺(tái)回復(fù)「555」即可獲取


          注明:僅僅作為知識(shí)分享,切勿用于其它商業(yè)活動(dòng) 。感謝所有技術(shù)分享者的付出。


          點(diǎn)贊是最大的支持 a79192677a42db8436f5e12e762f11bb.webp

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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片免费看 | 99视频在线观看视频 | 日韩中文字幕视频在线 | 欧美激情网站 | 女人18片毛片60分钟免费 |