countdownlatch 是基于 aqs 共享模式的一次性计数器,state 表示剩余等待数,归零后不可重置;semaphore 是基于 aqs 共享模式的可动态增减许可证池,state 表示可用许可证数,支持公平/非公平策略及批量操作。

CountDownLatch 和 Semaphore 的源码都基于 AQS(AbstractQueuedSynchronizer) 实现,但用途和设计逻辑不同。理解它们的关键不是逐行读源码,而是抓住「状态语义」和「AQS 模式选择」这两个核心。
CountDownLatch:共享模式下的一次性计数器
它用 AQS 的 共享锁模式,state 字段直接表示剩余等待数:
- 构造时调用
setState(count)初始化计数; -
countDown()调用releaseShared(1),尝试将 state 减 1;减到 0 时,tryReleaseShared返回 true,触发 AQS 唤醒所有阻塞线程; -
await()调用acquireSharedInterruptibly(1),若 state ≠ 0,则入队挂起;state 变为 0 后,tryAcquireShared返回 ≥0,线程直接通过; - state 归零后不可重置——
tryAcquireShared永远返回 1,后续 await 不再阻塞,这是“一次性”的本质体现。
Semaphore:共享模式下的可增可减许可证池
它也走 AQS 共享模式,但 state 表示当前可用许可证数量,支持 acquire/release 动态调整:
- 公平/非公平由内部 Sync 子类决定,公平版使用
FairSync,按 FIFO 唤醒; -
acquire()调用acquireSharedInterruptibly(1),若 state > 0 则 CAS 减 1 成功,否则排队; -
release()调用releaseShared(1),CAS 增加 state,并唤醒头结点后第一个共享节点; - 支持批量操作:
acquire(3)要求 state ≥ 3 才能获取,否则阻塞;release(2)直接给 state +2,可能唤醒多个等待线程。
对比看源码时最该盯住的三个地方
不用通读全部代码,重点关注:
-
Sync 内部类:CountDownLatch 只有
Sync extends AbstractQueuedSynchronizer;Semaphore 有两个:FairSync / NonfairSync,区别就在tryAcquireShared是否检查队列是否有前驱节点; - tryAcquireShared 方法:CountDownLatch 中它只判断 state == 0;Semaphore 中它要判断 state ≥ 需求数,且做 CAS 尝试;
- state 更新逻辑:CountDownLatch 的 state 只减不增、归零即终态;Semaphore 的 state 可增可减,且 release 可能导致多次唤醒。
调试建议:从构造 + await/acquire 入手
写个最小复现案例,打断点进:
- CountDownLatch 构造后,观察
sync.getState()初始值; - 调一次
countDown(),看sync.getState()是否减 1; - 在
await()处停住,看是否进入doAcquireSharedInterruptibly; - 对 Semaphore,重点验证
acquire()失败后是否真进了 AQS 队列,release()后是否真唤醒了下一个线程。











