java使用CountDownLatch實現多線程協(xié)作
前言
在多線程編程中,經常需要實現一種機制來協(xié)調多個線程的執(zhí)行,以確保某些操作在所有線程完成后再進行。CountDownLatch 就是 Java 并發(fā)包中提供的一種同步工具,它能夠讓一個或多個線程等待其他線程完成操作。
了解 CountDownLatch
概括
CountDownLatch 是Java 1.5版本推出的一個同步輔助類,在構造時需要指定一個計數值,該計數值表示需要等待的事件數量。每當一個事件完成時,計數值就會減一,當計數值減至零時,等待的線程就會被喚醒繼續(xù)執(zhí)行。
CountDownLatch 的應用場景
CountDownLatch 可以被廣泛應用于各種多線程協(xié)作的場景,例如:
- 主線程等待多個子線程完成后再執(zhí)行下一步操作。
- 多個子任務并行執(zhí)行,最后合并結果。
- 并行計算中,等待所有計算任務完成后進行統(tǒng)一匯總。
使用案例
讓我們通過一個示例代碼來理解 CountDownLatch 的使用。假設有一個任務需要被分配給多個子線程來完成,并且主線程需要等待所有子線程執(zhí)行完畢后才能繼續(xù)執(zhí)行。
//任務分割的線程數
private static final int THREAD_TOTAL = 10;
//子線程執(zhí)行的超時時間
private static final int countDownLatchTimeout = 5;
public static void main(String[] args) {
//創(chuàng)建CountDownLatch并設置計數值,該count值可以根據線程數的需要設置
CountDownLatch countDownLatch = new CountDownLatch(THREAD_TOTAL);
//創(chuàng)建線程池,開啟、創(chuàng)建異步線程執(zhí)行任務
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í)行超時時間)
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)缺點分析
優(yōu)點
- 簡單易用:CountDownLatch 的使用非常簡單,通過 await 和 countDown 方法即可實現多線程的協(xié)作。
- 靈活性:可以根據具體場景指定等待的計數值,可以靈活控制多個線程的協(xié)作關系。
- 高效性:底層使用了 AQS(AbstractQueuedSynchronizer)來實現同步,能夠保證高效地協(xié)調多個線程的執(zhí)行順序。
缺點
- 一次性:CountDownLatch 的計數值只能減少,無法重置。一旦計數值減至零,就不能再次使用。
- 無法中途取消:一旦等待開始,就無法中途取消等待,除非等待超時或者發(fā)生中斷。
如果您學有余力或手頭沒有著急的需求,請繼續(xù)往下看,讓我們簡單從源碼層面分析下CountDownLatch的實現。
從源碼層面分析CountDownLatch的實現
實現
我截取了CountDownLatch內部關鍵實現邏輯來分析其實現原理:
CountDownLatch的功能主要通過內部類Sync實現,在內部類中,Sync繼承自AbstractQueuedSynchronizer來實現同步操作,AbstractQueuedSynchronizer提供了同步器實現的基礎框架,通過該類,開發(fā)者可以相對容易地實現自定義的同步器,例如獨占鎖、共享鎖、信號量等。
/**
* 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 :定義了一個名為 Sync 的靜態(tài)內部類,它繼承自 AbstractQueuedSynchronizer 類,這個類通常被用于實現鎖和相關的同步器。
- count:定義了一個序列化版本號,用于在對象序列化和反序列化時進行版本控制。同時count在CountDownLatch的構造方法中用于設置當前狀態(tài),即:編碼人員傳入的計數值。
- getCount:獲取當前狀態(tài)值,即剩余的計數值。
- tryAcquireShared:嘗試獲取共享資源,如果當前狀態(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 方法嘗試釋放共享資源,首先通過一個無限循環(huán)不斷嘗試,在循環(huán)中獲取當前狀態(tài)值,如果狀態(tài)值已經為0,則直接返回false;否則將狀態(tài)值減1,并嘗試原子性地設置狀態(tài)值,如果設置成功,則返回是否狀態(tài)值變?yōu)?,否則繼續(xù)循環(huán)。
總的來說,這段代碼實現了一個簡單的 CountDownLatch 功能,通過 tryAcquireShared 方法嘗試獲取共享資源,通過 tryReleaseShared 方法嘗試釋放共享資源。當共享資源的狀態(tài)值為0時,表示所有等待的線程都已被釋放。
擴展
CompletableFuture簡述
在JDK 1.8后,java.util.concurrent包提供了CompletableFuture類用于支持異步編程和異步任務的處理,相較于CountDownLatch,它提供了更豐富的API,就個人而言,我更喜歡CompletableFuture,因為它擴展性強,更適合JDK 8提供的函數式編程特性,代碼更加優(yōu)雅。
CompletableFuture 的優(yōu)缺點
優(yōu)點
- 功能強大:CompletableFuture 提供了豐富的方法和組合操作,可以實現復雜的異步編程邏輯。
- 支持異常處理:可以通過 exceptionally 或 handle 方法方便地處理異步操作中的異常情況。
- 支持組合操作:可以通過 thenCompose、thenCombine 等方法方便地進行多個 CompletableFuture 的組合操作。
缺點
- 學習曲線較陡:相對于 CountDownLatch,CompletableFuture 的使用可能需要更多的學習和理解異步編程的概念。
- 復雜度較高:在復雜的業(yè)務場景下,可能會出現嵌套回調、異常處理困難等問題,增加了代碼的復雜度。
總結
CountDownLatch 和 CompletableFuture 都是 Java 中用于多線程協(xié)作的工具,它們各自適用于不同的場景。CountDownLatch 更適合簡單的多線程協(xié)作,而 CompletableFuture 則更適合復雜的異步編程場景。在實際應用中,我們可以根據具體的需求選擇合適的工具來實現多線程協(xié)作和異步編程,以達到更好的開發(fā)效率和代碼質量。
以上就是java使用CountDownLatch實現多線程協(xié)作的詳細內容,更多關于java CountDownLatch的資料請關注腳本之家其它相關文章!
相關文章
IDEA編譯報錯:Error:java:無效的源發(fā)行版:17的解決辦法
IDEA里面裝了幾個版本的JDK,導入工程后時不時提示一下錯誤,下面這篇文章主要給大家介紹了關于IDEA編譯報錯:Error:java:無效的源發(fā)行版:17的解決辦法,需要的朋友可以參考下2023-01-01

