CountDownLatch的await()只响应state==0,因其tryAcquireShared返回1(成功)仅当getState()==0,否则返回-1阻塞;countDown()用for(;;)确保CAS减state原子性,仅最后一次减至0的线程触发唤醒。

CountDownLatch 的 await() 为什么只响应 state == 0?
因为 tryAcquireShared 的返回值直接决定线程是否能“获取共享资源”:返回 ≥ 0 才算成功,而 CountDownLatch 的实现里只写死了 (getState() == 0) ? 1 : -1。它根本不关心“还有多少资源剩”,只关心“门关没关”——state 是 0 就代表所有 countDown() 已完成,门开了,放行。
这个设计意味着:
-
await()不是“抢资源”,而是“等信号”;它不参与计数过程,只观察最终状态 - 一旦 state 变为 0,所有在
doAcquireSharedInterruptibly中挂起的线程都会被唤醒(通过setHeadAndPropagate向后传播) - 没有“部分满足”逻辑:哪怕 state 从 5→1,只要不是 0,
await()就继续阻塞
countDown() 的 CAS 循环为何必须用 for(;;)?
tryReleaseShared 里那个死循环不是为了重试多次,而是为了确保 CAS 原子性更新成功。因为多个线程可能同时调用 countDown(),都读到同一个旧值 c,但只有一个能成功把 state 从 c 改成 c-1。失败者必须重读新 state 再试——否则会漏减或重复减。
常见错误是忽略返回值判断:return nextc == 0 这一行才是关键——只有最后一次减到 0 的那个线程,才会触发唤醒逻辑(AQS 的 doReleaseShared 流程)。其他线程减完就默默退出。
为什么 await() 被中断后不能简单 ignore?
acquireSharedInterruptibly 在入口就检查 Thread.interrupted(),一旦发现已中断,立刻抛 InterruptedException。这不是“礼貌提醒”,而是 AQS 共享模式的契约:中断必须立即响应,不能让线程卡在队列里假装没发生。
容易踩的坑:
- 在
catch (InterruptedException)里只打印日志却不恢复中断状态(Thread.currentThread().interrupt()),会导致上层无法感知中断意图 - 误以为
await(long, TimeUnit)超时后线程就“安全了”——其实超时返回false后,如果后续还有countDown(),该线程仍可能被唤醒,需自行处理状态一致性
任务对齐场景下,state 初始值设错会导致什么?
初始 count 必须严格等于实际要等待的 countDown() 总次数。少设了,await() 永远不会返回;多设了,主线程永远卡住——而且这种错误**不会抛异常,也不会告警**,只会静默 hang 住。
典型陷阱:
- 异步任务中,部分分支没走到
countDown()(比如异常提前 return) - 使用线程池时,任务提交失败或被拒绝,但没对应补偿
countDown() - 误把
CyclicBarrier的“可重置”特性套用到CountDownLatch上,试图复用已触发的实例
真正难调试的从来不是代码写法,而是那个没被执行的 countDown() ——它藏在某个 if 分支、finally 外、或者被 swallow 的 catch 块里。

















