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

Java 併發核心:CountDownLatch 同步倒計時器深度解析與實戰

     編輯:Java 綜合教學

Java 併發核心:CountDownLatch 同步倒計時器深度解析與實戰

CountDownLatch 是 Java 併發包(java.util.concurrent)中一個極具實用價值的同步輔助類,其工作原理類似「倒計時計數器」:初始化時設定計數值,呼叫 await() 的線程會阻塞等待,每呼叫一次 countDown() 計數減一,當計數歸零時,所有等待線程被同時喚醒繼續執行。

核心 API 速覽

方法說明
CountDownLatch(int count)構造函數,設定初始計數值
void countDown()計數器減一,若歸零則喚醒所有等待線程
void await()阻塞當前線程,直到計數歸零或被中斷
boolean await(long timeout, TimeUnit unit)帶超時的等待,超時返回 false
long getCount()獲取當前計數值

經典應用場景:多線程賽跑模擬

以下完整範例模擬 5 位運動員等待發令槍響同時起跑,主線程等待所有運動員到達終點:

package basic;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestCountDown {
    private static final int PLAYER_AMOUNT = 5;

    public static void main(String[] args) {
        // 發令槍:計數為 1,所有選手 await() 等待
        CountDownLatch begin = new CountDownLatch(1);
        // 終點線:計數為 5,每位選手完成 countDown(),主線程 await()
        CountDownLatch end = new CountDownLatch(PLAYER_AMOUNT);

        Player[] players = new Player[PLAYER_AMOUNT];
        for (int i = 0; i < PLAYER_AMOUNT; i++) {
            players[i] = new Player(i + 1, begin, end);
        }

        ExecutorService executorService = Executors.newFixedThreadPool(PLAYER_AMOUNT);
        for (Player player : players) {
            executorService.execute(player);
        }

        System.out.println("比賽即將開始...");
        // 發令槍響:計數歸零,喚醒所有選手
        begin.countDown();

        try {
            // 主線程等待所有選手到達終點
            end.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("比賽結束!");
        }
        executorService.shutdown();
    }
}

class Player implements Runnable {
    private final int id;
    private final CountDownLatch begin;
    private final CountDownLatch end;

    public Player(int id, CountDownLatch begin, CountDownLatch end) {
        this.id = id;
        this.begin = begin;
        this.end = end;
    }

    @Override
    public void run() {
        try {
            // 等待發令槍
            begin.await();
            // 模擬跑步時間
            Thread.sleep((long) (Math.random() * 100));
            System.out.println("選手 " + id + " 到達終點。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 到達終點,計數減一
            end.countDown();
        }
    }
}

執行流程解析

  1. 初始化:begin=1(發令槍),end=5(終點)
  2. 選手線程啟動:全部阻塞在 begin.await()
  3. 主線程呼叫 begin.countDown():計數歸零,5 位選手同時被喚醒開始跑步
  4. 選手跑完:在 finally 區塊呼叫 end.countDown()
  5. 主線程:阻塞在 end.await(),直到計數歸零(5→0)被喚醒,打印「比賽結束」

源碼級洞察:await() 實現原理

await() 內部委託給同步器(Sync 繼承 AbstractQueuedSynchronizer)的 acquireSharedInterruptibly(1):

  • 若計數已為 0,立即返回
  • 否則將當前線程封裝為節點加入同步隊列掛起
  • 被喚醒條件:計數歸零(countDown() 呼叫 releaseShared(1))或線程中斷
  • 中斷拋出 InterruptedException 並清除中斷狀態

這種基於 AQS 共享模式的實現,保證了高效、可中斷的同步語義。

常見誤區與最佳實踐

  • 不可重用:計數歸零後無法重置,需新建實例(循環場景請用 CyclicBarrier)
  • 計數不可為負:多呼叫 countDown() 不會拋異常但計數停在 0,邏輯上應避免
  • 線程安全:內部基於 AQS 實現,多線程呼叫安全
  • 異常處理:countDown() 建議放在 finally 區塊,確保計數必遞減

適用場景總結

  • 主線程等待多個子線程完成初始化任務
  • 多線程並行計算,彙總結果前需等待全部完成
  • 模擬並發啟動、壓力測試的「起跑線」控制
  • 替代傳統 Thread.join(),提供更靈活的控制粒度
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved