可以模拟CountDownLatch核心行为:用synchronized+while循环+wait()/notifyAll()实现await()阻塞与countDown()唤醒,但非线程安全,仅用于理解;真实场景应使用JDK原生CountDownLatch。

可以在 Java 中用 while 循环配合 synchronized 和 wait()/notifyAll() 模拟 CountDownLatch 的核心行为,但要注意:这不是线程安全的替代实现,仅用于理解其阻塞/唤醒逻辑。真实场景应直接使用 JDK 提供的 CountDownLatch。
核心思路:用计数器 + 等待条件模拟 await()
关键在于让调用线程在计数未归零时进入等待状态,其他线程每调用一次 countDown() 就尝试唤醒所有等待者。
- 定义一个 volatile 或 synchronized 保护的计数器(如
private int count) -
await()方法中用while (count > 0)循环判断,不满足则wait() -
countDown()中递减计数器,并在变为 0 时调用notifyAll()
基础同步版实现(支持单次 await)
以下是一个最小可行示例,使用 synchronized 块保证可见性和原子性:
public class SimpleCountDownLatch {
private int count;
<pre class="brush:php;toolbar:false;">public SimpleCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException();
this.count = count;
}
public void await() throws InterruptedException {
synchronized (this) {
while (count > 0) {
this.wait(); // 释放锁并等待
}
}
}
public void countDown() {
synchronized (this) {
if (count > 0) {
count--;
if (count == 0) {
this.notifyAll(); // 唤醒所有等待线程
}
}
}
}}
立即学习“Java免费学习笔记(深入)”;
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
⚠️ 注意:await() 可能被虚假唤醒,所以必须用 while 而不是 if 判断条件。
支持中断和超时的增强逻辑(简略示意)
原生 CountDownLatch.await(long, TimeUnit) 支持超时,而 Object.wait(long) 可实现类似效果:
- 在
await(long timeout, TimeUnit unit)中,将超时转为毫秒,调用wait(timeoutMs) - 每次
wait()返回后重新检查count == 0,若仍大于 0 且未超时,继续等待 - 需记录起始时间、计算剩余时间,避免因虚假唤醒导致无限等待
- 若等待中被中断,应抛出
InterruptedException并恢复中断状态
为什么不能只靠 while(true) 自旋?
纯 while (count > 0) { /* 空循环 */ } 是忙等待,会持续占用 CPU,且无法保证内存可见性:
- 没有同步机制时,一个线程修改
count,另一个线程可能永远看不到新值(JVM 指令重排或缓存不一致) - 即使加
volatile,自旋仍浪费 CPU;wait()才是真正的阻塞+唤醒协作模型 - 必须配合
synchronized(或Lock)才能合法调用wait()/notify()
这种模拟有助于理解 AQS 底层的等待队列与状态变更逻辑,但生产环境请始终使用 java.util.concurrent.CountDownLatch —— 它基于 AQS 实现,支持公平性、中断响应、超时控制,并经过充分测试和优化。

















