countdownlatch用于控制多线程同步启动与完成,而非精确计时;通过startlatch实现几乎同时启动(微秒级误差),donelatch确保全部执行完毕,避免sleep或volatile等不可靠对齐方式。

在单元测试中用 CountDownLatch 捕捉多线程并行启动的“瞬时时差”,关键不是让它“更准”,而是**控制线程启动的同步点**,消除非确定性调度干扰。它本身不测量时间,但能帮你构造出可复现的竞态场景。
用 CountDownLatch 实现“同时启动”信号
多个线程需在**同一逻辑时刻**开始执行(比如模拟并发请求),此时让它们都等待一个共享的倒计时锁存器,主线程统一释放——这是最可靠的方式:
- 初始化
CountDownLatch startLatch = new CountDownLatch(1) - 每个工作线程启动后立即调用
startLatch.await()阻塞 - 主线程在所有线程
start()后,调用startLatch.countDown() - 所有线程将几乎同时脱离阻塞,进入业务逻辑(误差通常在微秒级,远小于 JVM 调度抖动)
配合第二个 CountDownLatch 等待全部完成
仅控制启动还不够,你还得确认所有线程真正执行完毕,才能断言结果。这时用第二个 latch 做“完成门控”:
- 初始化
CountDownLatch doneLatch = new CountDownLatch(threadCount) - 每个工作线程在业务逻辑末尾调用
doneLatch.countDown() - 主线程调用
doneLatch.await(5, TimeUnit.SECONDS)等待超时保障 - 若超时,说明有线程卡死或未执行完,测试失败,避免假阳性
避免常见陷阱:别混用 sleep 或 volatile 自旋
有人试图用 Thread.sleep(1) 或轮询 volatile 标志位来“对齐”启动,这不可靠:
-
sleep受系统时钟精度、GC 暂停、线程调度延迟影响,毫秒级误差很常见 - volatile 自旋浪费 CPU,且无法保证“所有线程都在同一纳秒看到 true”
-
CountDownLatch底层基于 AQS,唤醒是内核级通知,无忙等、无时序漂移 - 注意:不要在同一个 latch 上重复 await 或 countDown,它是一次性的
验证“瞬时时差”的实际效果(可选)
如果真想观察线程启动的时间分布,可在每个线程里记录 System.nanoTime():
- 在
startLatch.await()返回后立刻记录时间戳 - 收集所有线程的戳,计算最大差值(max - min)
- 典型值在 10–100 微秒量级(取决于 CPU 和负载),远优于手动 sleep 对齐
- 该数据仅用于调试或文档,不建议作为断言条件(环境敏感)










