JUC系列學(xué)習(xí)工具類CountDownLatch詳解
前言:
項(xiàng)目中我們經(jīng)常會(huì)遇到有時(shí)候需要等待其他線程完成任務(wù)后,主線程才能執(zhí)行其他任務(wù),那么我們將如何實(shí)現(xiàn)呢?
Join 解決方案
join 的工作原理是,檢查thread是否存活,如果存活則讓當(dāng)前線程永遠(yuǎn)wait,直到 thread線程終止,線程的 notifyAll才會(huì)被調(diào)用。
具體實(shí)現(xiàn)
public class JoinAThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " 線程開(kāi)始"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( Thread.currentThread().getName() + " 線程執(zhí)行完畢"); } } public class JoinBThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " 線程開(kāi)始"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( Thread.currentThread().getName() + " 線程執(zhí)行完畢"); } } public class JoinTest { public static void main(String[] args) throws InterruptedException { JoinAThread joinA =new JoinAThread(); Thread threadA =new Thread(joinA,"線程A"); JoinBThread joinB =new JoinBThread(); Thread threadB =new Thread(joinB,"線程B"); threadA.start(); threadB.start(); threadA.join(); threadB.join(); System.out.println("子線程執(zhí)行完成了,主線程"+Thread.currentThread().getName()+"開(kāi)始執(zhí)行了"); } }
執(zhí)行結(jié)果
從結(jié)果中,我們可以看出只有子線程執(zhí)行完成了,主線程才開(kāi)始執(zhí)行。join的實(shí)現(xiàn)我們需要每個(gè)線程進(jìn)行join,如果存在多個(gè)線程,那么寫(xiě)起來(lái)會(huì)比較的繁瑣,那么又沒(méi)更新優(yōu)化的方案了,答案是JUC下面的工具類CountDownLatch,也能完成同樣的功能。
CountDownLatch 解決方案
具體實(shí)現(xiàn)
public class CountDownLatchTest { private static Logger logger =LoggerFactory.getLogger(CountDownLatchTest.class); public static void main(String[] args) throws InterruptedException { ExecutorService exec = Executors.newCachedThreadPool(); final CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 1; i <= 10; i++){ exec.execute(() -> { try { invokeServiec(); } catch (InterruptedException e) { logger.info("invoce service error",e); } finally { //計(jì)數(shù)器減一 countDownLatch.countDown(); } }); } countDownLatch.await(); logger.info("所有的子線程執(zhí)行完成,主線程"+Thread.currentThread().getName()+"開(kāi)始執(zhí)行"); } private static void invokeServiec() throws InterruptedException { logger.info(Thread.currentThread().getName()+",開(kāi)始執(zhí)行任務(wù)"); Thread.sleep(300); } }
說(shuō)明:CountDownLatch中有兩個(gè)方法一個(gè)是await()方法,調(diào)用這個(gè)方法的線程會(huì)被阻塞,另外一個(gè)是countDown() 方法,調(diào)用此方法會(huì)使計(jì)數(shù)器減一,當(dāng)計(jì)數(shù)器的值為0時(shí),調(diào)用await()方法被阻塞的線程才會(huì)被喚醒。
執(zhí)行結(jié)果:
原理說(shuō)明
CountDownLatch 是一個(gè)計(jì)數(shù)器閉鎖,通過(guò)它可以完成類似于阻塞當(dāng)前線程的功能,即:一個(gè)線程或多個(gè)線程一直等待,直到其他線程執(zhí)行的操作完成。
基本原理
CountDownLatch
CountDownLatch內(nèi)部定義計(jì)數(shù)器和一個(gè)隊(duì)列。當(dāng)計(jì)數(shù)器的值遞減為0之前,阻塞隊(duì)列里面的線程處于掛起狀態(tài),當(dāng)計(jì)數(shù)器遞減到0時(shí)會(huì)喚醒阻塞隊(duì)列所有線程,計(jì)數(shù)器是一個(gè)標(biāo)志,可以表示一個(gè)任務(wù)一個(gè)線程,也可以表示一個(gè)倒計(jì)時(shí)器。
常用的方法
countDown:用于使計(jì)數(shù)器減一,其一般是執(zhí)行任務(wù)的線程調(diào)用. await: 使用線程處于等待狀態(tài),其一般是主線程調(diào)用.
countDown
countDown實(shí)現(xiàn)方法如下:
說(shuō)明:sync是一個(gè)AQS的隊(duì)列,調(diào)用的為AQS的releaseShared方法,其具體實(shí)現(xiàn)如下:
而releaseShared調(diào)用為CountDownLatch中的內(nèi)部類sync中的tryReleaseShared方法,具體實(shí)現(xiàn)如下:
tryReleaseShared(int)方法即對(duì)state屬性進(jìn)行減一操作的代碼.通過(guò)CAS進(jìn)行減操作來(lái)保證原子性,其會(huì)比較state是否為c,如果是則將其設(shè)置為nextc(自減1),如果state不為c,則說(shuō)明有另外的線程在getState()方法和compareAndSetState()方法調(diào)用之間對(duì)state進(jìn)行了設(shè)置,當(dāng)前線程也就沒(méi)有成功設(shè)置state屬性的值,其會(huì)進(jìn)入下一次循環(huán)中,如此往復(fù),直至其成功設(shè)置state屬性的值,即countDown()方法調(diào)用成功。
而doReleaseShared方法調(diào)用的為AbstractQueuedSynchronizer簡(jiǎn)稱AQS的doReleaseShared方法,
說(shuō)明:首先判斷頭結(jié)點(diǎn)不為空,且不為尾節(jié)點(diǎn),說(shuō)明等待隊(duì)列中有等待喚醒的線程,這里需要說(shuō)明的是,在等待隊(duì)列中,頭節(jié)點(diǎn)中并沒(méi)有保存正在等待的線程,其只是一個(gè)空的Node對(duì)象,真正等待的線程是從頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)開(kāi)始存放的,因而會(huì)有對(duì)頭結(jié)點(diǎn)是否等于尾節(jié)點(diǎn)的判斷。在判斷等待隊(duì)列中有正在等待的線程之后,其會(huì)清除頭結(jié)點(diǎn)的狀態(tài)信息,并且調(diào)用unparkSuccessor(Node)方法喚醒頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),使其繼續(xù)往下執(zhí)行。如下是unparkSuccessor(Node)方法的具體實(shí)現(xiàn):
可以看到,unparkSuccessor(Node)方法的作用是喚醒離傳入節(jié)點(diǎn)最近的一個(gè)處于等待狀態(tài)的線程,使其繼續(xù)往下執(zhí)行。
await
await方法實(shí)現(xiàn)如下:
await()方法調(diào)用了Sync對(duì)象的方法acquireSharedInterruptibly(int)方法,該方法的具體實(shí)現(xiàn)如下:
在doAcquireSharedInterruptibly(int)方法中,首先使用當(dāng)前線程創(chuàng)建一個(gè)共享模式的節(jié)點(diǎn)。然后在一個(gè)for循環(huán)中判斷當(dāng)前線程是否獲取到執(zhí)行權(quán)限,如果有(r >= 0判斷)則將當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn),并且喚醒后續(xù)處于共享模式的節(jié)點(diǎn);如果沒(méi)有,則對(duì)調(diào)用shouldParkAfterFailedAcquire(Node, Node)和parkAndCheckInterrupt()方法使當(dāng)前線程處于"擱置"狀態(tài),該"擱置"狀態(tài)是由操作系統(tǒng)進(jìn)行的,這樣可以避免該線程無(wú)限循環(huán)而獲取不到執(zhí)行權(quán)限,造成資源浪費(fèi),這里也就是線程處于等待狀態(tài)的位置,也就是說(shuō)當(dāng)線程被阻塞的時(shí)候就是阻塞在這個(gè)位置。當(dāng)有多個(gè)線程調(diào)用await()方法而進(jìn)入等待狀態(tài)時(shí),這幾個(gè)線程都將等待在此處。
總結(jié)
本文對(duì)JUC的工具類CountDownLatch進(jìn)行詳細(xì)的講解,如有疑問(wèn)請(qǐng)隨時(shí)反饋。
到此這篇關(guān)于JUC系列學(xué)習(xí)工具類CountDownLatch詳解的文章就介紹到這了,更多相關(guān)JUC工具類CountDownLatch 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot雪花算法主鍵ID傳到前端后精度丟失問(wèn)題的解決
本文主要介紹了SpringBoot雪花算法主鍵ID傳到前端后精度丟失問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08SpringBoot框架實(shí)現(xiàn)切換啟動(dòng)開(kāi)發(fā)環(huán)境和測(cè)試環(huán)境
這篇文章主要介紹了SpringBoot框架實(shí)現(xiàn)切換啟動(dòng)開(kāi)發(fā)環(huán)境和測(cè)試環(huán)境,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Java鎖的升級(jí)策略 偏向鎖 輕量級(jí)鎖 重量級(jí)鎖
在本文中小編給的大家整理了關(guān)于Java鎖的升級(jí)策略 偏向鎖 輕量級(jí)鎖 重量級(jí)鎖的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們參考下。2019-06-06spring boot 防止重復(fù)提交實(shí)現(xiàn)方法詳解
這篇文章主要介紹了spring boot 防止重復(fù)提交實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了spring boot 防止重復(fù)提交具體配置、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2019-11-11springboot3請(qǐng)求參數(shù)種類及接口測(cè)試案例小結(jié)
這篇文章主要介紹了springboot3請(qǐng)求參數(shù)種類及接口測(cè)試案例小結(jié),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10攔截JSP頁(yè)面,校驗(yàn)是否已登錄詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了攔截JSP頁(yè)面,校驗(yàn)是否已登錄詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2016-11-11Java的springcloud Sentinel是什么你知道嗎
這篇文章主要介紹了Java之springcloud Sentinel案例講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08java判斷對(duì)象中某個(gè)屬性是否為空方法代碼
這篇文章主要給大家介紹了關(guān)于java判斷對(duì)象中某個(gè)屬性是否為空的相關(guān)資料,最近遇到后臺(tái)接收值的時(shí)候,需要對(duì)接收對(duì)象進(jìn)行非空校驗(yàn),需要的朋友可以參考下2023-07-07