CountDownLatch 的 state 是 AQS 中 volatile int 类型的真实字段,初始值即构造参数,countDown() 通过 CAS 减 1,await() 通过 tryAcquireShared 判断 state 是否为 0 并挂起线程,仅 state 减至 0 时触发唤醒,且不可重用。

CountDownLatch 的 state 就是倒计时数字
它不是抽象概念,而是 AQS 里一个真实的 volatile int state 字段。你传入构造函数的数字(比如 new CountDownLatch(3)),就直接赋值给这个 state。它从不隐藏——每次调用 countDown(),就是对 state 做一次 CAS 减 1;每次调用 await(),就是检查 state == 0 是否成立。
这个变量被 volatile 修饰,意味着所有线程都能立刻看到它的最新值,不会因为 CPU 缓存导致误判。它是整个同步逻辑唯一的状态锚点。
await() 不是“主动等待”,而是“登记+挂起”
主线程或任意线程调用 await() 时,并不会轮询检查计数器,而是:
- 先尝试调用
tryAcquireShared(1)—— 它只做一件事:返回(getState() == 0) ? 1 : -1 - 如果
state != 0,就返回 -1,触发后续流程:addWaiter(Node.SHARED)把当前线程包装成共享模式节点,插入 AQS 的 CLH 双向等待队列尾部 - 然后调用
parkAndCheckInterrupt(),让线程真正休眠,释放 CPU
此时线程不再消耗资源,只是安静地待在队列里,等着别人来“叫醒”。它不抢、不查、不猜,纯粹靠状态变更驱动。
countDown() 是唯一能“敲门唤醒”的操作
每个子线程完成任务后调用 countDown(),本质是执行 releaseShared(1):
- 内部反复 CAS 尝试将
state减 1,直到成功 - 减完后判断:如果新值恰好为 0,就进入唤醒逻辑 ——
doReleaseShared() -
doReleaseShared()会从 AQS 队列头开始,遍历并唤醒所有处于SHARED模式的等待节点(即所有调用了await()的线程)
注意:只有把 state 从 1 减到 0 的那个线程,才会真正触发唤醒动作。其他线程的 countDown() 只负责“扣分”,不负责“开门”。
不可重用不是设计缺陷,而是状态机的自然终点
state 归零后,tryAcquireShared() 总返回 1,await() 立刻返回;而 tryReleaseShared() 在 state == 0 时直接返回 false,拒绝再减 —— 这意味着锁已“耗尽”。
这不是 bug,是共享锁状态机的确定性表现:
- 初始态:
state = N > 0 - 中间态:
state ∈ [1, N−1],有线程在等 - 终态:
state = 0,所有 await 返回,后续 countDown 无效
想再次使用?必须新建实例 —— 因为状态机已经走到了终点,没有“复位”指令。










