你的回答(口语化,面试场景)
面试官:什么是 Java 的 CountDownLatch
?
你:
好的,这个问题我结合项目中的使用场景来回答。
-
一句话定义
CountDownLatch
是 Java 并发包里的一个工具类,让一个或多个线程等待其他线程完成操作后再继续执行。它像一个倒计时门闩,主线程通过“倒数归零”触发后续动作。 -
核心方法
countDown()
:减少计数器(通常由子线程调用,表示任务完成)。await()
:阻塞当前线程(通常是主线程),直到计数器归零。
- 使用场景
- 主线程等待子线程初始化完成:比如微服务启动时,等待所有模块加载完毕再对外提供服务。
- 高并发压测:模拟1000个用户同时发起请求(所有线程就绪后统一触发)。
- 多任务汇总:多个线程计算数据,主线程汇总结果。
- 代码示例
// 主线程等待3个子线程完成任务
CountDownLatch latch = new CountDownLatch(3); // 子线程执行任务
executor.submit(() -> { loadDataFromDB(); latch.countDown(); // 任务完成,计数器减1
});
// 类似地提交另外2个线程 // 主线程等待所有子线程完成
latch.await();
System.out.println("所有数据加载完成,开始处理!");
- 对比 CyclicBarrier
- 单向触发:
CountDownLatch
主线程等子线程,且不可重置重用。 - 计数器角色:子线程只负责减计数,主线程等待;而
CyclicBarrier
是线程互相等待并触发回调。
预测面试官可能的追问及回答
追问1:如果子线程中忘记调用 countDown()
,会发生什么?
回答:
- 主线程会一直阻塞在
await()
,导致程序卡死。 - 解决:用
try-finally
确保countDown()
执行,例如:try { doWork(); } finally { latch.countDown(); }
追问2:CountDownLatch
的计数器可以重置吗?
回答:
- 不能。
CountDownLatch
设计为一次性,计数器归零后无法重置。 - 如果需要重用,应该用
CyclicBarrier
。
知识框架与底层原理补充
- 底层实现(AQS)
- 计数器:内部通过 AQS 的
state
字段保存计数。 - 阻塞机制:调用
await()
的线程进入 AQS 的等待队列,state
归零时唤醒所有线程。
-
常见陷阱
| 问题 | 原因与解决方案 |
|-------------------|-----------------------------------------------------------------------------------|
| 死锁 | 子线程未调用countDown()
→ 用try-finally
兜底。 |
| 计数设置错误 | 初始化时计数器大于实际子线程数 → 严格匹配任务数和计数。 |
| 过度等待 | 子线程执行时间过长 → 使用await(long timeout, TimeUnit unit)
添加超时控制。 | -
实战案例
场景:电商订单支付后,需同时通知库存系统、物流系统和积分系统,全部成功后更新订单状态。
方案: -
主线程创建
CountDownLatch(3)
。 -
三个子线程分别调用库存、物流、积分系统的接口,执行完成时调用
countDown()
。 -
主线程
await()
等待,若全部成功则更新订单,否则回滚。
// 伪代码
CountDownLatch latch = new CountDownLatch(3);
executor.submit(() -> { try { notifyInventorySystem(); } finally { latch.countDown(); }
});
// 物流、积分系统类似 boolean success = latch.await(5, TimeUnit.SECONDS);
if (success) { updateOrderStatus();
} else { rollback();
}
- 性能优化点
- 避免主线程长时间阻塞:合理设置超时时间,结合异步回调处理部分失败场景。
- 替代方案:对于复杂依赖场景,可用
CompletableFuture.allOf()
实现更灵活的异步编排。
总结
- CountDownLatch 是多线程协作的“发令枪”,适用于主线程等待子线程就绪或完成的场景。
- 核心是计数器不可逆,使用时需严格匹配任务数与初始计数,并通过异常处理防止死锁。
- 理解 AQS 底层逻辑(如
state
和队列管理),能更好应对高并发下的性能调优问题。 - 区分与 CyclicBarrier 的差异,根据“一次性 vs 可重用”、“主等子 vs 子等子”选择合适工具。