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

Java CountDownLatch 核心原理與實戰應用場景全解析

     編輯:Java 相關知識

什麼是 CountDownLatch

CountDownLatch 是一個同步計數器,構造時指定計數初始值。每調用一次 countDown() 計數減 1,當計數歸零時,所有調用 await() 等待的線程被釋放繼續執行。常用於「主線程等待多個子線程完成」或「多線程同步起跑」場景。

核心 API

  • CountDownLatch(int count):構造器,設置初始計數
  • void countDown():計數減 1,計數歸零喚醒等待線程
  • void await():阻塞當前線程,直到計數歸零
  • boolean await(long timeout, TimeUnit unit):帶超時等待
  • long getCount():獲取當前計數值

經典場景一:模擬賽跑發令槍與終點判定

public class RaceSimulation {
    private static final int PLAYER_COUNT = 5;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);   // 發令槍
        CountDownLatch finishSignal = new CountDownLatch(PLAYER_COUNT); // 終點計數

        ExecutorService executor = Executors.newFixedThreadPool(PLAYER_COUNT);
        
        for (int i = 1; i <= PLAYER_COUNT; i++) {
            final int playerNo = i;
            executor.submit(() -> {
                try {
                    startSignal.await(); // 等待發令
                    Thread.sleep((long) (Math.random() * 1000)); // 模擬跑步時間
                    System.out.println("選手 " + playerNo + " 到達終點");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    finishSignal.countDown(); // 到達終點計數
                }
            });
        }

        System.out.println("比賽開始!");
        startSignal.countDown(); // 發令
        
        finishSignal.await(); // 主線程等待所有人到達
        System.out.println("比賽結束!");
        executor.shutdown();
    }
}

經典場景二:並行初始化多個服務組件

public class ServiceInitializer {
    private final CountDownLatch latch;
    private final List<Service> services = new ArrayList<>();

    public ServiceInitializer(int serviceCount) {
        this.latch = new CountDownLatch(serviceCount);
    }

    public void addService(Service service) {
        services.add(service);
        new Thread(() -> {
            try {
                service.init(); // 耗時初始化
            } finally {
                latch.countDown();
            }
        }).start();
    }

    public void waitForAllReady() throws InterruptedException {
        latch.await(); // 阻塞直到所有服務就緒
        System.out.println("所有服務初始化完成");
    }
}

進階用法:結合 CyclicBarrier 實現分階段同步

CountDownLatch 一次性計數,不可重置。若需循環同步(如多輪迭代計算),請使用 CyclicBarrier 或 Phaser。

常見誤區與最佳實踐

  1. 不可重用:計數歸零後不可重置,需新建實例。
  2. 異常處理:countDown() 必須在 finally 塊調用,防止異常導致計數不減造成永久阻塞。
  3. 計數匹配:構造器計數必須等於實際調用 countDown() 的次數,否則會過早釋放或永久等待。
  4. 非線程安全變量:getCount() 僅供監控調試,不用於同步控制邏輯。

源碼核心邏輯(AQS 實現)

基於 AbstractQueuedSynchronizer (AQS) 共享鎖模式:

  • state 表示計數值
  • countDown() → releaseShared(1) 嘗試釋放共享鎖
  • await() → acquireSharedInterruptibly(1) 獲取共享鎖,state>0 時入隊阻塞

總結

CountDownLatch 是輕量級同步工具,專為「一組線程等待另一組線程完成」設計。掌握其計數語義與 AQS 原理,能優雅解決並行任務協調、服務啟動同步、分階段計算等核心並發問題。

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