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

Java 並發編程利器:CountDownLatch 原理與實戰範例詳解

     編輯:關於C語言

前言

在多線程開發中,常會遇到「主線程需等待多個子線程完成任務後再繼續執行」的場景。從 JDK 1.5 起,java.util.concurrent.CountDownLatch 提供了一種優雅的同步機制來解決此類問題。本文將深入淺出地介紹其核心原理、關鍵方法及實戰應用。

核心概念:同步倒數計數器

CountDownLatch 內部維護一個計數器,初始值由建構子傳入的 int 參數決定。它有兩個核心操作:

  • countDown():計數器減 1。通常由工作者線程在完成任務時調用。
  • await():阻塞當前線程,直到計數器歸零。通常由主線程或協調線程調用。

此外,還提供帶超時的 await(long timeout, TimeUnit unit),若超時則不再阻塞,無論計數器是否歸零。

經典場景模擬:等學生上車

假設一輛車需等 10 位同學上車,設定等待 3 秒。若 3 秒內全上齊則發車;否則時間到直接開走,剩下的同學只能看著車尾燈發呆。

完整代碼實現

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class CountDownLatchTest {
    public static int numberOfPeople = 10;
    public static volatile boolean isGone = false; // 使用 volatile 保證可見性
    public static int carWaitTime = 3;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch waitStudentsGetOn = new CountDownLatch(numberOfPeople);

        // 啟動上車線程
        new Thread(new GetOn(waitStudentsGetOn)).start();

        // 主線程等待上車
        waitStudentGetOn(waitStudentsGetOn);

        // 發車
        driveHome();
    }

    private static void waitStudentGetOn(CountDownLatch latch) throws InterruptedException {
        System.out.println("趕緊的,抓緊時間上車..");
        // 等待 3 秒,時間到自動返回
        latch.await(carWaitTime, TimeUnit.SECONDS);
    }

    private static void driveHome() throws InterruptedException {
        System.out.println("開車走囉~ (鞋兒破 帽兒破...)");
        isGone = true;
    }
}

class GetOn implements Runnable {
    private final CountDownLatch latch;

    GetOn(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        for (int i = 0; i < CountDownLatchTest.numberOfPeople; i++) {
            try {
                // 若車已走,剩餘同學無法上車
                if (CountDownLatchTest.isGone) {
                    System.out.println("媽的,還差:" + latch.getCount() + " 個沒娃上車呢,車走了!");
                    break;
                }

                boolean success = new Student(i + 1).getOn();
                if (success) {
                    latch.countDown(); // 計數器減一
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }

            // 即時反饋剩餘人數
            if (latch.getCount() != 0) {
                System.out.println("還差:" + latch.getCount() + " 個沒上車");
            } else {
                System.out.println("都上車了!");
            }
        }
    }

    static class Student {
        private final int id;

        Student(int id) { this.id = id; }

        boolean getOn() throws InterruptedException {
            // 模擬上車耗時 0~1 秒
            Thread.sleep(new Random().nextInt(2) * 1000L);
            if (CountDownLatchTest.isGone) return false;
            System.out.print("編號為:" + id + "的同學上車了.. ");
            return true;
        }
    }
}

關鍵點解析

  1. volatile 關鍵字:isGone 標記需加 volatile,確保多線程間的可見性,避免指令重排導致邏輯錯誤。
  2. 異常處理:await() 和 sleep() 會拋出 InterruptedException,捕獲後應恢復中斷狀態 (Thread.currentThread().interrupt()) 或退出循環,否則中斷信號會丟失。
  3. 一次性使用:CountDownLatch 計數器歸零後無法重置,若需循環使用請改用 CyclicBarrier。

總結

CountDownLatch 以極簡的 API 解決了「一對多」線程同步等待問題,適用於應用啟動初始化、並行任務聚合等場景。掌握其「倒數鎖」語義,能讓並發控制代碼更清晰、健壯。

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