CountDownLatch闭锁触发后await()立即返回的原理是:其内部基于AQS,await()每次调用均无条件检查state(计数器值),state为0时直接返回true,不入队、不阻塞;且state不可重置,故此后所有await()均走该快速路径。

闭锁触发后 await() 立即返回的原理
闭锁(CountDownLatch)的核心行为是:一旦内部计数器归零(即被 countDown() 调用减至 0),所有**正在阻塞等待**的 await() 会立即唤醒并返回;更重要的是,**此后所有新调用的 await() 都不再阻塞,直接返回 true**。这不是“缓存结果”,而是由其内部状态机决定的——它只关心当前计数是否为 0,不关心调用时机。
用原生代码验证“后续 await() 总是立即放行”
下面是一段不依赖任何框架、仅用 JDK 原生线程与同步工具的可运行示例,重点观察闭锁触发后新线程调用 await() 的行为:
关键设计点:
- 主线程创建
CountDownLatch(1),初始计数为 1 - 启动一个子线程,在其中调用
await()并记录开始/结束时间 - 主线程休眠 100ms 后调用
countDown()—— 此时闭锁被触发 - 主线程立刻(毫秒级内)再新建一个线程,再次调用
await() - 对比两个线程中
await()的耗时:第一个应阻塞约 100ms,第二个应接近 0ms
可直接运行的验证代码:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
<p>public class CountDownLatchProof {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);</p><pre class="brush:php;toolbar:false;"> // 第一个线程:await() 在 latch 未触发前调用 → 应阻塞
Thread t1 = new Thread(() -> {
long start = System.nanoTime();
try {
latch.await(); // 阻塞直到 countDown()
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long end = System.nanoTime();
System.out.printf("t1 await() 耗时: %.2f ms%n", (end - start) / 1_000_000.0);
});
t1.start();
// 主线程等待 100ms,再触发闭锁
TimeUnit.MILLISECONDS.sleep(100);
latch.countDown();
System.out.println("✅ 闭锁已触发(countDown() 已调用)");
// 立即启动第二个线程:在闭锁已触发后调用 await()
Thread t2 = new Thread(() -> {
long start = System.nanoTime();
try {
boolean result = latch.await(5, TimeUnit.SECONDS); // 不会阻塞
long end = System.nanoTime();
System.out.printf("t2 await() 返回 %s,耗时: %.2f ms%n", result, (end - start) / 1_000_000.0);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
t2.start();
t1.join();
t2.join();
}}
典型输出(JDK 8+):
t1 await() 耗时: 100.34 ms ✅ 闭锁已触发(countDown() 已调用) t2 await() 返回 true,耗时: 0.03 ms
这证明:只要闭锁已被触发(计数为 0),后续任意时刻调用 await() 都不会进入阻塞队列,而是立即检查状态并返回 true。
为什么能保证“总是立即放行”?看 JDK 源码逻辑
CountDownLatch.await() 底层基于 AQS(AbstractQueuedSynchronizer)。其 tryAcquireShared 方法核心逻辑就是:
- 读取当前 state(即剩余计数)
- 若 state == 0 → 直接返回 1(表示获取成功,不阻塞)
- 若 state > 0 → 尝试入队并挂起线程
这个判断是**无锁、无条件、每次调用都执行**的。state 归零后永远不会恢复,所以后续所有 await() 都走“立即返回”分支。
注意:这不是“唤醒遗留线程”的副作用,而是设计契约
有人误以为“后续 await() 放行”是因为 AQS 队列被清空或信号被广播。其实不是。即使没有线程曾调用过 await(),只要先调用 countDown(),之后第一次 await() 依然瞬间返回。它的语义是:“等待计数归零”,而不是“等待某个特定事件的通知”。一旦条件满足,所有检查都自然通过。

















