java使用CountDownLatch實(shí)現(xiàn)多線程協(xié)作
前言
在多線程編程中,經(jīng)常需要實(shí)現(xiàn)一種機(jī)制來協(xié)調(diào)多個(gè)線程的執(zhí)行,以確保某些操作在所有線程完成后再進(jìn)行。CountDownLatch 就是 Java 并發(fā)包中提供的一種同步工具,它能夠讓一個(gè)或多個(gè)線程等待其他線程完成操作。
了解 CountDownLatch
概括
CountDownLatch 是Java 1.5版本推出的一個(gè)同步輔助類,在構(gòu)造時(shí)需要指定一個(gè)計(jì)數(shù)值,該計(jì)數(shù)值表示需要等待的事件數(shù)量。每當(dāng)一個(gè)事件完成時(shí),計(jì)數(shù)值就會(huì)減一,當(dāng)計(jì)數(shù)值減至零時(shí),等待的線程就會(huì)被喚醒繼續(xù)執(zhí)行。
CountDownLatch 的應(yīng)用場景
CountDownLatch 可以被廣泛應(yīng)用于各種多線程協(xié)作的場景,例如:
- 主線程等待多個(gè)子線程完成后再執(zhí)行下一步操作。
- 多個(gè)子任務(wù)并行執(zhí)行,最后合并結(jié)果。
- 并行計(jì)算中,等待所有計(jì)算任務(wù)完成后進(jìn)行統(tǒng)一匯總。
使用案例
讓我們通過一個(gè)示例代碼來理解 CountDownLatch 的使用。假設(shè)有一個(gè)任務(wù)需要被分配給多個(gè)子線程來完成,并且主線程需要等待所有子線程執(zhí)行完畢后才能繼續(xù)執(zhí)行。
//任務(wù)分割的線程數(shù)
private static final int THREAD_TOTAL = 10;
//子線程執(zhí)行的超時(shí)時(shí)間
private static final int countDownLatchTimeout = 5;
public static void main(String[] args) {
//創(chuàng)建CountDownLatch并設(shè)置計(jì)數(shù)值,該count值可以根據(jù)線程數(shù)的需要設(shè)置
CountDownLatch countDownLatch = new CountDownLatch(THREAD_TOTAL);
//創(chuàng)建線程池,開啟、創(chuàng)建異步線程執(zhí)行任務(wù)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < THREAD_TOTAL; i++) {
cachedThreadPool.execute(() -> {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + " do something!");
} catch (Exception e) {
System.out.println("Exception: do something exception");
} finally {
//該線程執(zhí)行完畢-1
countDownLatch.countDown();
}
});
}
//回到主線程中
System.out.println("Back main thread do something");
try {
//主線程等待線程池中完成(子線程執(zhí)行超時(shí)時(shí)間)
boolean await = countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
System.out.println(await);
} catch (InterruptedException e) {
System.out.println("Exception: await interrupted exception");
} finally {
System.out.println("countDownLatch: " + countDownLatch);
}
System.out.println("main thread do something-2");
}

CountDownLatch 的優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn)
- 簡單易用:CountDownLatch 的使用非常簡單,通過 await 和 countDown 方法即可實(shí)現(xiàn)多線程的協(xié)作。
- 靈活性:可以根據(jù)具體場景指定等待的計(jì)數(shù)值,可以靈活控制多個(gè)線程的協(xié)作關(guān)系。
- 高效性:底層使用了 AQS(AbstractQueuedSynchronizer)來實(shí)現(xiàn)同步,能夠保證高效地協(xié)調(diào)多個(gè)線程的執(zhí)行順序。
缺點(diǎn)
- 一次性:CountDownLatch 的計(jì)數(shù)值只能減少,無法重置。一旦計(jì)數(shù)值減至零,就不能再次使用。
- 無法中途取消:一旦等待開始,就無法中途取消等待,除非等待超時(shí)或者發(fā)生中斷。
如果您學(xué)有余力或手頭沒有著急的需求,請繼續(xù)往下看,讓我們簡單從源碼層面分析下CountDownLatch的實(shí)現(xiàn)。
從源碼層面分析CountDownLatch的實(shí)現(xiàn)
實(shí)現(xiàn)
我截取了CountDownLatch內(nèi)部關(guān)鍵實(shí)現(xiàn)邏輯來分析其實(shí)現(xiàn)原理:
CountDownLatch的功能主要通過內(nèi)部類Sync實(shí)現(xiàn),在內(nèi)部類中,Sync繼承自AbstractQueuedSynchronizer來實(shí)現(xiàn)同步操作,AbstractQueuedSynchronizer提供了同步器實(shí)現(xiàn)的基礎(chǔ)框架,通過該類,開發(fā)者可以相對容易地實(shí)現(xiàn)自定義的同步器,例如獨(dú)占鎖、共享鎖、信號量等。
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
- Sync :定義了一個(gè)名為 Sync 的靜態(tài)內(nèi)部類,它繼承自 AbstractQueuedSynchronizer 類,這個(gè)類通常被用于實(shí)現(xiàn)鎖和相關(guān)的同步器。
- count:定義了一個(gè)序列化版本號,用于在對象序列化和反序列化時(shí)進(jìn)行版本控制。同時(shí)count在CountDownLatch的構(gòu)造方法中用于設(shè)置當(dāng)前狀態(tài),即:編碼人員傳入的計(jì)數(shù)值。
- getCount:獲取當(dāng)前狀態(tài)值,即剩余的計(jì)數(shù)值。
- tryAcquireShared:嘗試獲取共享資源,如果當(dāng)前狀態(tài)為0,則返回1表示成功獲取資源,否則返回-1表示獲取資源失敗。
tryReleaseShared
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
tryReleaseShared 方法嘗試釋放共享資源,首先通過一個(gè)無限循環(huán)不斷嘗試,在循環(huán)中獲取當(dāng)前狀態(tài)值,如果狀態(tài)值已經(jīng)為0,則直接返回false;否則將狀態(tài)值減1,并嘗試原子性地設(shè)置狀態(tài)值,如果設(shè)置成功,則返回是否狀態(tài)值變?yōu)?,否則繼續(xù)循環(huán)。
總的來說,這段代碼實(shí)現(xiàn)了一個(gè)簡單的 CountDownLatch 功能,通過 tryAcquireShared 方法嘗試獲取共享資源,通過 tryReleaseShared 方法嘗試釋放共享資源。當(dāng)共享資源的狀態(tài)值為0時(shí),表示所有等待的線程都已被釋放。
擴(kuò)展
CompletableFuture簡述
在JDK 1.8后,java.util.concurrent包提供了CompletableFuture類用于支持異步編程和異步任務(wù)的處理,相較于CountDownLatch,它提供了更豐富的API,就個(gè)人而言,我更喜歡CompletableFuture,因?yàn)樗鼣U(kuò)展性強(qiáng),更適合JDK 8提供的函數(shù)式編程特性,代碼更加優(yōu)雅。
CompletableFuture 的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 功能強(qiáng)大:CompletableFuture 提供了豐富的方法和組合操作,可以實(shí)現(xiàn)復(fù)雜的異步編程邏輯。
- 支持異常處理:可以通過 exceptionally 或 handle 方法方便地處理異步操作中的異常情況。
- 支持組合操作:可以通過 thenCompose、thenCombine 等方法方便地進(jìn)行多個(gè) CompletableFuture 的組合操作。
缺點(diǎn)
- 學(xué)習(xí)曲線較陡:相對于 CountDownLatch,CompletableFuture 的使用可能需要更多的學(xué)習(xí)和理解異步編程的概念。
- 復(fù)雜度較高:在復(fù)雜的業(yè)務(wù)場景下,可能會(huì)出現(xiàn)嵌套回調(diào)、異常處理困難等問題,增加了代碼的復(fù)雜度。
總結(jié)
CountDownLatch 和 CompletableFuture 都是 Java 中用于多線程協(xié)作的工具,它們各自適用于不同的場景。CountDownLatch 更適合簡單的多線程協(xié)作,而 CompletableFuture 則更適合復(fù)雜的異步編程場景。在實(shí)際應(yīng)用中,我們可以根據(jù)具體的需求選擇合適的工具來實(shí)現(xiàn)多線程協(xié)作和異步編程,以達(dá)到更好的開發(fā)效率和代碼質(zhì)量。
以上就是java使用CountDownLatch實(shí)現(xiàn)多線程協(xié)作的詳細(xì)內(nèi)容,更多關(guān)于java CountDownLatch的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式
在Web應(yīng)用程序開發(fā)中,統(tǒng)一數(shù)據(jù)返回格式對于前后端分離項(xiàng)目尤為重要,本文就來介紹一下SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
SSM如何實(shí)現(xiàn)在Controller中添加事務(wù)管理
這篇文章主要介紹了SSM如何實(shí)現(xiàn)在Controller中添加事務(wù)管理,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java調(diào)用Deepseek實(shí)現(xiàn)項(xiàng)目代碼審查
這篇文章主要為大家詳細(xì)介紹了Java如何調(diào)用Deepseek實(shí)現(xiàn)項(xiàng)目代碼審查功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02
java?Object轉(zhuǎn)Integer實(shí)現(xiàn)方式
這篇文章主要介紹了java?Object轉(zhuǎn)Integer實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
IDEA編譯報(bào)錯(cuò):Error:java:無效的源發(fā)行版:17的解決辦法
IDEA里面裝了幾個(gè)版本的JDK,導(dǎo)入工程后時(shí)不時(shí)提示一下錯(cuò)誤,下面這篇文章主要給大家介紹了關(guān)于IDEA編譯報(bào)錯(cuò):Error:java:無效的源發(fā)行版:17的解決辦法,需要的朋友可以參考下2023-01-01
SpringBoot操作Mongodb的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot操作Mongodb的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06

