好的,我来为你详细讲解 Java 并发工具类 CountDownLatch 的理解和使用 👇


1. 什么是 CountDownLatch

CountDownLatch 是 java.util.concurrent 包下的一个同步辅助类,常用于 线程等待协调 的场景。

它的主要功能是:

  • 一个或多个线程 等待 其他线程完成一组操作 后再继续执行。

通俗理解:像一个倒计时锁,初始化一个计数值(count),每次调用 countDown() 方法,计数器减 1;当计数器为 0 时,等待的线程会被唤醒继续执行。


2. 构造方法

public CountDownLatch(int count)

  • count:初始计数值,表示需要多少个事件完成后才能继续。

3. 常用方法

  1. await()
    • 让当前线程等待,直到计数器为 0 才继续。
    • 可带超时参数:await(long timeout, TimeUnit unit)
  2. countDown()
    • 将计数器减 1,表示完成一个任务。
    • 当计数器减到 0 时,所有等待的线程会被释放。
  3. getCount()
    • 获取当前剩余的计数值。

4. 应用场景

  • 并行任务等待汇总:主线程等待多个子任务完成。
  • 模拟并发场景:比如压测,多个线程同时执行。
  • 控制线程执行顺序:比如让线程 B 等待线程 A 执行完成。

5. 使用示例

示例 1:等待多个任务完成

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int taskCount = 3;
        CountDownLatch latch = new CountDownLatch(taskCount);

        for (int i = 0; i < taskCount; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始执行任务...");
                try {
                    Thread.sleep(1000); // 模拟任务执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 完成任务!");
                latch.countDown(); // 任务完成,计数器减一
            }).start();
        }

        System.out.println("主线程等待所有子任务完成...");
        latch.await(); // 等待计数器归零
        System.out.println("所有子任务已完成,主线程继续执行。");
    }
}

运行结果:

Thread-0 开始执行任务...
Thread-1 开始执行任务...
Thread-2 开始执行任务...
主线程等待所有子任务完成...
Thread-0 完成任务!
Thread-1 完成任务!
Thread-2 完成任务!
所有子任务已完成,主线程继续执行。


示例 2:模拟并发执行(类似并发测试)

import java.util.concurrent.CountDownLatch;

public class ConcurrencyTest {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 5;
        CountDownLatch startSignal = new CountDownLatch(1); // 开始信号
        CountDownLatch doneSignal = new CountDownLatch(threadCount); // 结束信号

        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                try {
                    startSignal.await(); // 等待开始信号
                    System.out.println(Thread.currentThread().getName() + " 同时开始执行任务");
                    Thread.sleep((long)(Math.random() * 2000));
                    doneSignal.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        System.out.println("所有线程准备就绪,3 秒后同时开始...");
        Thread.sleep(3000);
        startSignal.countDown(); // 释放开始信号
        doneSignal.await(); // 等待所有线程完成
        System.out.println("所有线程任务执行完毕!");
    }
}


6. 注意事项

  1. CountDownLatch 是一次性的,计数器一旦归零,就不能重置。
    • 如果需要重复使用,可以考虑 CyclicBarrier 或 Semaphore
  2. 调用 countDown() 不会阻塞线程,它只是减少计数器。
  3. await() 才是阻塞当前线程的方法。

✅ 总结一句话:
CountDownLatch 适合用于 “一个或多个线程等待一组操作完成后再执行” 的场景。