一句话总结
CountDownLatch 用于同步线程,允许一个或多个线程等待其他线程完成操作。初始化时设定计数器,线程完成任务后调用 countDown() 减少计数,当计数器归零时,等待的线程被唤醒。常用于主线程等待多个子线程初始化完成,或控制多个线程同时开始执行任务。
详细解析
CountDownLatch 是 Java 并发包(java.util.concurrent)中的同步工具类,其核心作用是 协调多个线程的执行顺序,允许一个或多个线程等待其他线程完成操作后再继续执行。以下是其核心用途、工作原理及典型场景的详细解析:
一、核心功能与作用
- 等待多个线程完成
主线程(或某个线程)需要等待多个子线程执行完毕后再继续处理后续逻辑。
示例:主线程等待所有服务初始化完成后再启动业务逻辑。 - 控制并行任务的起始点
多个线程在某一时刻同时开始执行任务(如赛跑发令枪)。
示例:同时启动多个线程进行数据处理,确保所有线程同时进入执行阶段。 - 实现最大并行性
通过等待所有线程就绪后同时释放,最大化资源利用率。
二、工作原理
- 计数器机制
o 初始化时设定一个计数值count,表示需要等待的任务数量。 - o 每个任务完成后调用countDown()方法将计数器减 1。
- o 当计数器减至 0 时,所有调用await()的线程被唤醒。
- 底层依赖 AQS
基于AbstractQueuedSynchronizer(AQS)实现,通过同步状态(state)管理计数器,线程阻塞和唤醒由 AQS 的队列控制。
三、典型应用场景
1、主线程等待子线程完成
场景:主线程需要汇总多个子线程的计算结果。
代码示例:
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 子线程执行任务
latch.countDown();
}).start();
}
latch.await(); // 主线程等待所有子线程完成
System.out.println("所有子线程已完成,主线程继续执行");
输出:
子线程1完成
子线程2完成
子线程3完成
所有子线程已完成,主线程继续执行
2、多服务依赖启动
场景:主服务需等待数据库、缓存等依赖服务启动完成后才能运行。
代码示例:
CountDownLatch serviceLatch = new CountDownLatch(3);
// 启动数据库服务
new Thread(() -> {
startDatabase();
serviceLatch.countDown();
}).start();
// 启动缓存服务
new Thread(() -> {
startCache();
serviceLatch.countDown();
}).start();
serviceLatch.await(); // 等待所有服务启动
startMainService();
3、模拟并发测试
场景:模拟多个用户同时发起请求,测试系统性能。
代码示例:
CountDownLatch startLatch = new CountDownLatch(1); // 发令枪
CountDownLatch endLatch = new CountDownLatch(100); // 等待100个线程完成
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
startLatch.await(); // 等待发令枪响
sendRequest(); // 模拟请求
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endLatch.countDown();
}
}).start();
}
startLatch.countDown(); // 开始测试
endLatch.await(); // 等待所有请求完成
4、任务分阶段执行
场景:将任务拆分为多个阶段,每个阶段需等待前一阶段完成。
示例:
CountDownLatch phase1Latch = new CountDownLatch(5);
CountDownLatch phase2Latch = new CountDownLatch(5);
// 阶段1:数据预处理
for (int i = 0; i < 5; i++) {
new Thread(() -> {
preprocessData();
phase1Latch.countDown();
}).start();
}
phase1Latch.await(); // 等待阶段1完成
// 阶段2:数据分析
for (int i = 0; i < 5; i++) {
new Thread(() -> {
analyzeData();
phase2Latch.countDown();
}).start();
}
phase2Latch.await(); // 等待阶段2完成
四、与 CyclicBarrier 的区别
特性 | CountDownLatch | CyclicBarrier |
计数器重置 | 一次性使用,计数器归零后不可重置 | 可重复使用,计数器归零后自动重置 |
等待方向 | 一个或多个线程等待其他线程完成 | 所有线程相互等待,直到全部到达屏障点 |
典型用途 | 任务汇总、服务依赖启动 | 分阶段任务协调(如多阶段计算) |