await()是“等归零才放行”,countdown()是“减一不唤醒,直到归零才批量通知”;前者检查state是否为0决定是否入队挂起,后者通过cas递减并在state变为0时广播唤醒所有await线程。

await() 是“等归零才放行”,countDown() 是“减一不唤醒,直到归零才批量通知”——这是区分两者底层行为最核心的一句话。
await() 的本质:共享模式下的条件等待
调用 await() 的线程不会立刻执行,而是检查内部计数器 state 是否为 0:
- 如果 state == 0,直接通过,不入队
- 如果 state > 0,则基于 AQS(AbstractQueuedSynchronizer)以共享模式加入同步等待队列,挂起线程
- 它不争抢锁,也不修改 state,只是“守着门看数字”
- 多个线程同时 await(),会全部排队,但共用同一把共享锁
countDown() 的本质:原子减一 + 归零时触发唤醒
每次调用 countDown() 都尝试对 state 做一次 CAS 原子递减:
- state > 0 时:成功减一,但不唤醒任何线程
- state == 1 时:减为 0,此时才真正触发“释放共享资源”逻辑
- 一旦 state 变为 0,AQS 会遍历整个等待队列,将所有因 await() 而阻塞的线程一次性唤醒
- 注意:countDown() 可被任意线程多次调用,甚至同一线程重复调用,只要没超初始值,都有效
关键细节:为什么不是“减一就唤醒一个”?
CountDownLatch 的设计目标是“等全部完成”,不是“逐个放行”:
- 它没有“许可池”概念,不像 Semaphore 那样可增可减、按需分配
- 它的 state 是一次性消耗型计数器,归零即终态,不可重置(reset)
- 唤醒动作只在 state 从 1→0 这一刻发生,且是广播式唤醒全部 await 线程,不是点对点通知
一个小例子帮你印证
设 CountDownLatch latch = new CountDownLatch(3):
- 线程 A、B、C 同时调用 latch.await() → 全部入 AQS 队列挂起
- 线程 D 调用一次 countDown() → state 变为 2,无唤醒
- 线程 E 再调用一次 countDown() → state 变为 1,仍无唤醒
- 线程 F 第三次调用 countDown() → state 变为 0,AQS 立即唤醒 A、B、C 三个线程











