Java多線程同步工具類CountDownLatch詳解
簡(jiǎn)介
CountDownLatch
是一個(gè)多線程同步工具類,在多線程環(huán)境中它允許多個(gè)線程處于等待狀態(tài),直到前面的線程執(zhí)行結(jié)束。從類名上看CountDown
既是數(shù)量遞減的意思,我們可以把它理解為計(jì)數(shù)器。
核心方法
countDown()
:計(jì)數(shù)器遞減方法。await()
:使調(diào)用此方法的線程進(jìn)入等待狀態(tài),直到計(jì)數(shù)器計(jì)數(shù)為0時(shí)主線程才會(huì)被喚醒。await(long, TimeUnit)
:在await()
方法的基礎(chǔ)上增加了超時(shí)策略,若等待超時(shí)仍未有結(jié)果則會(huì)直接喚醒主線程運(yùn)行。
CountDownLatch如何使用
在這里我們用一段簡(jiǎn)單的代碼進(jìn)行演示:
@Slf4j public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(3); new Thread(() -> { log.info("hello this is thread one"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch.countDown(); }).start(); new Thread(() -> { log.info("hello this is thread two"); countDownLatch.countDown(); }).start(); new Thread(() -> { log.info("hello this is thread three"); countDownLatch.countDown(); }).start(); countDownLatch.await(); log.info("say good bye!"); } }
由上面的代碼可見(jiàn),我們創(chuàng)建了一個(gè)CountDownLatch
計(jì)數(shù)器為3和三個(gè)線程同步運(yùn)行。在main主線程中調(diào)用了countDownLatch.await()
方法使主線程進(jìn)入阻塞。其中三個(gè)線程任務(wù)執(zhí)行完畢后都會(huì)調(diào)用countDownLatch.countDown()
方法對(duì)計(jì)數(shù)器進(jìn)行遞減,當(dāng)三個(gè)線程任務(wù)都執(zhí)行完畢后計(jì)數(shù)器計(jì)數(shù)值為0時(shí)主線程被喚醒。
注:在創(chuàng)建
CountDownLatch
實(shí)例時(shí)必須定義計(jì)數(shù)器值,一般相對(duì)較合理的用法是該值的定義需要經(jīng)過(guò)合理的計(jì)算使計(jì)數(shù)值與需要并行的線程數(shù)相等,在每個(gè)線程執(zhí)行完成后做計(jì)數(shù)遞減,最終喚醒主線程繼續(xù)執(zhí)行。
CountDownLatch
計(jì)數(shù)值設(shè)置大于線程數(shù),那么最終所有線程都執(zhí)行完了,而計(jì)數(shù)為遞減到0那么主線程將會(huì)一直處于等待狀態(tài)。CountDownLatch
計(jì)數(shù)值設(shè)置小于并發(fā)線程數(shù),那么可能在部分線程未執(zhí)行完畢時(shí),計(jì)數(shù)就已經(jīng)遞減到0,則主線程會(huì)被提前喚醒。
CountDownLatch運(yùn)行流程
如下圖,主線程阻塞與喚醒的核心就是計(jì)數(shù)器,只有當(dāng)所有線程執(zhí)行完成計(jì)數(shù)逐個(gè)遞減最終才會(huì)喚起await()
阻塞中的主線程。
注:
await()
可以阻塞一個(gè)線程,也可以阻塞多個(gè)線程,如果是阻塞多個(gè)線程,那么在計(jì)數(shù)為0時(shí)將會(huì)喚醒所有被阻塞的線程。
運(yùn)用場(chǎng)景
在簡(jiǎn)單了解完CountDownLatch
的作用后,相信各位最終目的還是想了解如何去使用,在哪些場(chǎng)景下使用更加合適,接下來(lái)我就拿一個(gè)對(duì)賬業(yè)務(wù)的場(chǎng)景詳細(xì)分析一下。
相信現(xiàn)在很多平臺(tái)都會(huì)對(duì)接銀聯(lián)、微信、支付寶等支付渠道做交易,那么在這樣的場(chǎng)景下對(duì)賬是不可避免的。對(duì)賬通常都會(huì)在每日的凌晨去處理,一方面是凌晨時(shí)間點(diǎn)多數(shù)平臺(tái)訪問(wèn)量都會(huì)較小,服務(wù)器壓力也比較輕松,而且此時(shí)出賬也比較合理,所以在這個(gè)時(shí)間點(diǎn)做對(duì)賬也是一個(gè)大數(shù)據(jù)量計(jì)算的操作。
上面講這么多好像都沒(méi)說(shuō)到重點(diǎn),在處理對(duì)賬之前首先我們肯定是需要通過(guò)各個(gè)支付渠道獲取對(duì)賬單文件,那么該如何操作呢?
- 對(duì)賬文件下載(第一階段):在這種情況下可以設(shè)計(jì)三個(gè)任務(wù)并發(fā)去獲取對(duì)賬文件,使用
CountDownLatch
阻塞主線程,等待三個(gè)任務(wù)都獲取到文件的時(shí)候做計(jì)數(shù)遞減,最終喚醒主線將標(biāo)記本階段處理完成,并發(fā)起進(jìn)入下一階段的通知。 - 對(duì)賬文件解析(第二階段):在上個(gè)階段已下載完成的文件文件中,此階段要做的就是解析文件。由于三個(gè)渠道都是不同的廠家那么文件的內(nèi)容格式肯定都是不一樣的,這時(shí)候我們又可以使用
CountDownLatch
啟動(dòng)三個(gè)線程分別去解析各自的對(duì)賬文件,最終將文件內(nèi)容轉(zhuǎn)換為業(yè)務(wù)所需的數(shù)據(jù)統(tǒng)一格式入庫(kù),在三個(gè)任務(wù)都入庫(kù)完成后主線程又被喚醒標(biāo)記完成后,通知下一階段開(kāi)始進(jìn)入工作。 - 對(duì)賬結(jié)算(第三階段):在上一階段的數(shù)據(jù)入庫(kù)完成后,此階段要做的就是比對(duì)每一筆交易是否準(zhǔn)確,一般都是按單號(hào)與交易渠道比對(duì)交易的金額是否一致,如果金額一致則該筆交易結(jié)算成功,否則將交易判定為異常交易,并入庫(kù)處理。由上面的流程分析我們就可以設(shè)計(jì)相對(duì)合理的
CountDownLatch
計(jì)數(shù),結(jié)合Semaphore
信號(hào)量控制并發(fā)量同時(shí)對(duì)對(duì)比交易單做并發(fā)處理,最終帶所有交易單處理完成后喚醒主線程標(biāo)記對(duì)賬完成,并通知下一階段進(jìn)行出賬。 - 出賬(第四階段):通常平臺(tái)在對(duì)賬完成后會(huì)進(jìn)行出賬,也就是按照平臺(tái)的業(yè)務(wù)規(guī)則出具相關(guān)的賬單方便財(cái)務(wù)人員進(jìn)行統(tǒng)計(jì)。
總結(jié)
多線程并發(fā)的情況下需要做好同步處理,結(jié)合CountDownLatch
充分的運(yùn)用到業(yè)務(wù)場(chǎng)景當(dāng)中還是挺有必要的,凡是需要在多個(gè)任務(wù)執(zhí)行完成后再去做另一件事的情況都可以考慮使用CountDownLatch
,合理使用但請(qǐng)不要濫用,特別上面也提到過(guò)計(jì)數(shù)值需要確定,否則可能導(dǎo)致多任務(wù)無(wú)法做到同步甚至造成主線程無(wú)限等待。
到此這篇關(guān)于Java多線程同步工具類CountDownLatch詳解的文章就介紹到這了,更多相關(guān)Java CountDownLatch內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
prometheus監(jiān)控springboot應(yīng)用簡(jiǎn)單使用介紹詳解
這篇文章主要介紹了prometheus監(jiān)控springboot應(yīng)用簡(jiǎn)單使用介紹詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)
下面小編就為大家?guī)?lái)一篇Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動(dòng)生成的主鍵問(wèn)題
這篇文章主要介紹了mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動(dòng)生成的主鍵問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12java實(shí)現(xiàn)微博后臺(tái)登錄發(fā)送微博
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)微博后臺(tái)登錄發(fā)送微博的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-07-07解決restlet client報(bào)錯(cuò)No response.Is the cer
這篇文章主要介紹了解決restlet client報(bào)錯(cuò)No response.Is the certificate valid? Click here to check.問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Json轉(zhuǎn)化為Java對(duì)象的實(shí)例詳解
這篇文章主要介紹了Json轉(zhuǎn)化為Java對(duì)象的實(shí)例詳解的相關(guān)資料,前后端數(shù)據(jù)交互的情況經(jīng)常會(huì)遇到Json串與java 對(duì)象的相互轉(zhuǎn)換方便操作,需要的朋友可以參考下2017-08-08SpringBoot異常處理之異常顯示的頁(yè)面問(wèn)題
這篇文章主要介紹了SpringBoot異常處理異常顯示的頁(yè)面的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09Java實(shí)現(xiàn)單鏈表SingleLinkedList增刪改查及反轉(zhuǎn) 逆序等
單鏈表是鏈表的其中一種基本結(jié)構(gòu)。一個(gè)最簡(jiǎn)單的結(jié)點(diǎn)結(jié)構(gòu)如圖所示,它是構(gòu)成單鏈表的基本結(jié)點(diǎn)結(jié)構(gòu)。在結(jié)點(diǎn)中數(shù)據(jù)域用來(lái)存儲(chǔ)數(shù)據(jù)元素,指針域用于指向下一個(gè)具有相同結(jié)構(gòu)的結(jié)點(diǎn)。 因?yàn)橹挥幸粋€(gè)指針結(jié)點(diǎn),稱為單鏈表2021-10-10