首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 併發利器 CountDownLatch 原理深度解析與實戰應用

     編輯:Java 相關知識

CountDownLatch 核心概念

CountDownLatch 是一個同步輔助類,允許一個或多個線程等待其他線程完成操作。它基於 AQS(AbstractQueuedSynchronizer)的共享鎖模式實現,計數器不可重置,適合「一等多」場景。

CountDownLatch vs CyclicBarrier 核心差異

特性CountDownLatchCyclicBarrier
核心作用1或N個線程等待其他線程完成N個線程相互等待
計數器重置不可重置可重置(循環使用)
適用場景主線程等待多個子線程初始化完成多線程分階段計算,需同步進入下一階段

核心 API 詳解

// 構造函數:初始化計數器
public CountDownLatch(int count)

// 等待計數器歸零(可中斷)
public void await() throws InterruptedException

// 帶超時等待
public boolean await(long timeout, TimeUnit unit) throws InterruptedException

// 計數器遞減
public void countDown()

// 獲取當前計數
public long getCount()

底層實現原理:基於 AQS 共享鎖

CountDownLatch UML 類圖

1. 初始化:狀態設置

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

Sync(int count) {
    setState(count); // AQS state 即為計數器
}

2. await() - 共享鎖獲取流程

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

// AQS 獲取共享鎖邏輯
public final void acquireSharedInterruptibly(long arg) throws InterruptedException {
    if (Thread.interrupted()) throw new InterruptedException();
    if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);
}

// CountDownLatch 重寫:state==0 才能獲取鎖
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

當計數器 > 0 時,線程進入 CLH 隊列阻塞;計數器歸零時,所有等待線程被喚醒。

3. countDown() - 釋放共享鎖

public void countDown() {
    sync.releaseShared(1);
}

// AQS 釋放共享鎖
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

// CAS 遞減 state,歸零時返回 true 觸發喚醒
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0) return false;
        int nextc = c - 1;
        if (compareAndSetState(c, nextc)) return nextc == 0;
    }
}

實戰示例:主線程等待 5 個子線程完成

public class CountDownLatchDemo {
    private static final int LATCH_SIZE = 5;
    private static CountDownLatch doneSignal;

    public static void main(String[] args) throws InterruptedException {
        doneSignal = new CountDownLatch(LATCH_SIZE);
        
        // 啟動 5 個工作線程
        for (int i = 0; i < LATCH_SIZE; i++) {
            new WorkerThread().start();
        }
        
        System.out.println("主線程等待開始...");
        doneSignal.await(); // 阻塞直到計數器歸零
        System.out.println("主線程等待結束,繼續執行...");
    }

    static class WorkerThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(1000); // 模擬業務處理
                System.out.println(Thread.currentThread().getName() + " 完成任務");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                doneSignal.countDown(); // 計數器 -1
            }
        }
    }
}

執行結果分析

主線程等待開始...
Thread-0 完成任務
Thread-2 完成任務
Thread-1 完成任務
Thread-4 完成任務
Thread-3 完成任務
主線程等待結束,繼續執行...

5 個子線程並發執行,各自調用 countDown() 將計數器從 5 遞減至 0,主線程 await() 返回繼續執行。這是典型的「主線程等待多線程初始化完成」場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved