面试知识库
进阶

CountDownLatch与CyclicBarrier#

一句话答案#

CountDownLatch 一次性递减等多任务完成(一个等多个),CyclicBarrier 可复用让多线程互相等待到齐(多个互等)。

核心要点

CountDownLatch(倒计时门闩):

// 场景:主线程等待 N 个子任务全部完成后再继续

CountDownLatch latch = new CountDownLatch(3);  // 计数器 = 3

// 子任务完成后调用 countDown()
executor.submit(() -> { doTask1(); latch.countDown(); });
executor.submit(() -> { doTask2(); latch.countDown(); });
executor.submit(() -> { doTask3(); latch.countDown(); });

latch.await();  // 主线程阻塞,直到计数器减到 0
System.out.println("所有子任务完成");

// 特点:一次性的,计数器减到 0 后不能重置
// 典型场景:启动时等待多个服务初始化完成、并行查询多个数据源后汇总结果
java

CyclicBarrier(循环栅栏):

Semaphore(信号量):

三者对比:

维度CountDownLatchCyclicBarrierSemaphore
等待方式一个线程等待其他 N 个线程N 个线程互相等待控制并发数量
是否可重用否(一次性)是(自动重置)
计数器方向递减到 0 触发递增到 N 触发获取/释放许可
底层实现AQS(共享锁)ReentrantLock + ConditionAQS(共享锁)
典型场景主线程等子任务完成多阶段并行计算资源并发数限制
面试回答(2分钟版)

CountDownLatch 和 CyclicBarrier 都是并发协调工具,但使用场景不同。CountDownLatch 是一次性的倒计时门闩,典型场景是主线程等待多个子任务全部完成后再继续,比如启动时等多个服务初始化或并行查询多个数据源后汇总结果。它的计数器只能递减不能重置,countDown 减到零后 await 放行。CyclicBarrier 是可复用的循环栅栏,典型场景是多线程分阶段并行计算,所有线程到达栅栏后同时继续下一阶段,到达后计数器自动重置,适合多轮迭代场景。底层实现上,CountDownLatch 基于 AQS 的共享锁模式,CyclicBarrier 基于 ReentrantLock 加 Condition。简单记:CountDownLatch 是一个等多个,用完即废;CyclicBarrier 是多个互相等,可以反复使用。另外 Semaphore 用于控制并发数量限流,三者各有分工。

追问与易错

追问方向:

  • “CountDownLatch 能重置吗?”→ 不能,计数器减到 0 后无法重置,是一次性工具;需要重复使用的场景应选 CyclicBarrier
  • “CyclicBarrier 的 barrierAction 在哪个线程执行?”→ 在最后一个到达栅栏的线程中执行,不是新线程也不是主线程
  • “什么场景用哪个?”→ 主线程等子任务全部完成用 CountDownLatch,多线程分阶段互等用 CyclicBarrier,控制并发资源数用 Semaphore

易错点:

  • ❌ 混淆两者——CDL 一个等多个,CB 多个互等
  • ❌ CountDownLatch 的 countDown 只能调一次——可以多次