詳解Java多線程編程中CountDownLatch阻塞線程的方法
直譯過(guò)來(lái)就是倒計(jì)數(shù)(CountDown)門閂(Latch)。倒計(jì)數(shù)不用說(shuō),門閂的意思顧名思義就是阻止前進(jìn)。在這里就是指 CountDownLatch.await() 方法在倒計(jì)數(shù)為0之前會(huì)阻塞當(dāng)前線程。
CountDownLatch是一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待。
CountDownLatch 的作用和 Thread.join() 方法類似,可用于一組線程和另外一組線程的協(xié)作。例如,主線程在做一項(xiàng)工作之前需要一系列的準(zhǔn)備工作,只有這些準(zhǔn)備工作都完成,主線程才能繼續(xù)它的工作。這些準(zhǔn)備工作彼此獨(dú)立,所以可以并發(fā)執(zhí)行以提高速度。在這個(gè)場(chǎng)景下就可以使用 CountDownLatch 協(xié)調(diào)線程之間的調(diào)度了。在直接創(chuàng)建線程的年代(Java 5.0 之前),我們可以使用 Thread.join()。在 JUC 出現(xiàn)后,因?yàn)榫€程池中的線程不能直接被引用,所以就必須使用 CountDownLatch 了。
CountDownLatch類是一個(gè)同步計(jì)數(shù)器,構(gòu)造時(shí)傳入int參數(shù),該參數(shù)就是計(jì)數(shù)器的初始值,每調(diào)用一次countDown()方法,計(jì)數(shù)器減1,計(jì)數(shù)器大于0 時(shí),await()方法會(huì)阻塞程序繼續(xù)執(zhí)行。 CountDownLatch可以看作是一個(gè)倒計(jì)數(shù)的鎖存器,當(dāng)計(jì)數(shù)減至0時(shí)觸發(fā)特定的事件。利用這種特性,可以讓主線程等待子線程的結(jié)束。 下面以一個(gè)模擬運(yùn)動(dòng)員比賽的例子加以說(shuō)明。
CountDownLatch的一個(gè)非常典型的應(yīng)用場(chǎng)景是:有一個(gè)任務(wù)想要往下執(zhí)行,但必須要等到其他的任務(wù)執(zhí)行完畢后才可以繼續(xù)往下執(zhí)行。假如我們這個(gè)想要繼續(xù)往下執(zhí)行的任務(wù)調(diào)用一個(gè)CountDownLatch對(duì)象的await()方法,其他的任務(wù)執(zhí)行完自己的任務(wù)后調(diào)用同一個(gè)CountDownLatch對(duì)象上的countDown()方法,這個(gè)調(diào)用await()方法的任務(wù)將一直阻塞等待,直到這個(gè)CountDownLatch對(duì)象的計(jì)數(shù)值減到0為止。
CountDownLatch函數(shù)列表
CountDownLatch(int count) 構(gòu)造一個(gè)用給定計(jì)數(shù)初始化的 CountDownLatch。 // 使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線程被中斷。 void await() // 使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線程被中斷或超出了指定的等待時(shí)間。 boolean await(long timeout, TimeUnit unit) // 遞減鎖存器的計(jì)數(shù),如果計(jì)數(shù)到達(dá)零,則釋放所有等待的線程。 void countDown() // 返回當(dāng)前計(jì)數(shù)。 long getCount() // 返回標(biāo)識(shí)此鎖存器及其狀態(tài)的字符串。 String toString()
CountDownLatch數(shù)據(jù)結(jié)構(gòu)
CountDownLatch的UML類圖如下:
CountDownLatch的數(shù)據(jù)結(jié)構(gòu)很簡(jiǎn)單,它是通過(guò)"共享鎖"實(shí)現(xiàn)的。它包含了sync對(duì)象,sync是Sync類型。Sync是實(shí)例類,它繼承于AQS。
CountDownLatch的使用示例
下面通過(guò)CountDownLatch實(shí)現(xiàn):"主線程"等待"5個(gè)子線程"全部都完成"指定的工作(休眠1000ms)"之后,再繼續(xù)運(yùn)行。
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; public class CountDownLatchTest1 { private static int LATCH_SIZE = 5; private static CountDownLatch doneSignal; public static void main(String[] args) { try { doneSignal = new CountDownLatch(LATCH_SIZE); // 新建5個(gè)任務(wù) for(int i=0; i<LATCH_SIZE; i++) new InnerThread().start(); System.out.println("main await begin."); // "主線程"等待線程池中5個(gè)任務(wù)的完成 doneSignal.await(); System.out.println("main await finished."); } catch (InterruptedException e) { e.printStackTrace(); } } static class InnerThread extends Thread{ public void run() { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " sleep 1000ms."); // 將CountDownLatch的數(shù)值減1 doneSignal.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
運(yùn)行結(jié)果:
main await begin. Thread-0 sleep 1000ms. Thread-2 sleep 1000ms. Thread-1 sleep 1000ms. Thread-4 sleep 1000ms. Thread-3 sleep 1000ms. main await finished.
結(jié)果說(shuō)明:主線程通過(guò)doneSignal.await()等待其它線程將doneSignal遞減至0。其它的5個(gè)InnerThread線程,每一個(gè)都通過(guò)doneSignal.countDown()將doneSignal的值減1;當(dāng)doneSignal為0時(shí),main被喚醒后繼續(xù)執(zhí)行。
PS:CountDownLatch和CyclicBarrier的區(qū)別:
(1) CountDownLatch的作用是允許1或N個(gè)線程等待其他線程完成執(zhí)行;而CyclicBarrier則是允許N個(gè)線程相互等待。
(2) CountDownLatch的計(jì)數(shù)器無(wú)法被重置;CyclicBarrier的計(jì)數(shù)器可以被重置后使用,因此它被稱為是循環(huán)的barrier。
相關(guān)文章
mybatis-plus @DS實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源原理
本文主要介紹了mybatis-plus @DS實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07IDEA通過(guò)git回滾到某個(gè)提交節(jié)點(diǎn)或某個(gè)版本的操作方法
這篇文章主要介紹了IDEA通過(guò)git回滾到某個(gè)提交節(jié)點(diǎn)或某個(gè)版本的方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07Spring security實(shí)現(xiàn)對(duì)賬戶進(jìn)行加密
這篇文章主要介紹了Spring security實(shí)現(xiàn)對(duì)賬戶進(jìn)行加密,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03SpringBoot內(nèi)部調(diào)用事務(wù)不起作用問(wèn)題的解決方案
這篇文章主要介紹了SpringBoot事務(wù)不起作用問(wèn)題的解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java動(dòng)態(tài)代理之?dāng)r截器的應(yīng)用
今天小編就為大家分享一篇關(guān)于Java動(dòng)態(tài)代理之?dāng)r截器的應(yīng)用,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01mybatis-plus批量更新updateBatchById問(wèn)題
這篇文章主要介紹了mybatis-plus批量更新updateBatchById問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java并發(fā)編程之ConcurrentLinkedQueue源碼詳解
今天帶小伙伴們學(xué)習(xí)一下Java并發(fā)編程之Java ConcurrentLinkedQueue源碼,本篇文章詳細(xì)分析了ConcurrentLinkedQueue源碼,有代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助喲,需要的朋友可以參考下2021-05-05