countdownlatch 是一次性门栓,通过 aqs 实现不可重置的计数器同步:构造时指定任务数,countdown() 递减,await() 阻塞至归零后永久释放,适用于启动/结束屏障场景。

CountDownLatch 本质就是一次性门栓,它通过内部的计数器实现“等待全部就绪后统一放行”的同步逻辑。计数器初始化为正整数,每次调用 countDown() 减 1;线程调用 await() 会阻塞,直到计数器归零——此时所有等待线程被唤醒,且之后再调用 await() 将立即返回(不阻塞)。这个“归零即释放、不可重置”的特性,决定了它天然适合一次性同步场景。
核心机制:计数器归零即永久解锁
CountDownLatch 内部基于 AQS(AbstractQueuedSynchronizer)实现,其 state 字段作为计数器。一旦 state 变为 0,AQS 的共享锁状态即变为“可获取”,所有阻塞在 await() 的线程被唤醒并加入运行队列。关键点在于:state 归零后不会恢复,也不提供 reset() 方法——这是“一次性”的根本保障。
- 构造时传入的数值是“必须完成的任务数”或“需等待的参与者数”
-
countDown()是幂等操作:多次调用只在计数器 > 0 时有效;归零后再调用无任何效果 -
await()是阻塞等待:若计数器已为 0,则直接返回 true;否则挂起当前线程,直到被唤醒或超时/中断
典型使用模式:启动屏障与结束屏障
常见两类一次性同步需求,都靠同一 CountDownLatch 实现:
-
启动屏障(Start Barrier):主线程初始化资源后,发令“开始”,所有工作线程同时启动。例如:主线程创建好共享数据、启动 N 个线程,并调用一次
latch.countDown();每个工作线程在执行业务逻辑前先latch.await()——确保全部就位才并发执行 -
结束屏障(End Barrier):主线程等待多个异步任务全部完成。例如:启动 N 个线程处理子任务,每个线程结束前调用
latch.countDown();主线程调用latch.await()阻塞,直到 N 次递减使计数器归零,再汇总结果
注意事项:不可重用,异常需兜底
CountDownLatch 不是循环栅栏(CyclicBarrier 可重用),强行复用会导致逻辑错误:
- 不要试图通过反射修改 state 或新建同名变量伪装复用——破坏语义且易出错
- 若某线程因异常未执行
countDown(),计数器无法归零,await()将永远阻塞。建议配合超时机制:latch.await(30, TimeUnit.SECONDS),超时后主动检查状态或抛出异常 - 多个线程调用
countDown()是安全的,底层使用 CAS 保证递减原子性
替代选择:何时不该用 CountDownLatch
如果需要“多次重复等待-放行”,应选用 CyclicBarrier;如果要协调“一个线程等待另一线程到达某点”,更轻量的是 Exchanger 或 Phaser;而若只是简单通知单次事件(如服务启动完成),CompletableFuture 或 AtomicBoolean + wait/notify 可能更直观。CountDownLatch 的优势在于语义清晰、开销低、适合固定数量的协作场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











