countdownlatch可精准实现高并发测试中的同步起点,避免thread.sleep()因线程调度不可控导致的时序偏差;其通过初始化latch、线程await等待、主线程countdown释放三步达成“齐发”效果。

用 CountDownLatch 实现并发单元测试,核心是让多个线程在同一起点“齐发”,真实模拟高并发下对共享变量或接口的冲击场景。
为什么不用 Thread.sleep() 控制并发?
单纯起多个线程 + sleep 等待再执行,时序不可控:线程调度延迟、JVM 优化、系统负载都会导致实际执行时间分散,根本达不到“同时”效果。CountDownLatch 能精准实现“全部就绪 → 一声令下 → 同时开跑”的同步栅栏机制。
关键三步:准备、等待、释放
在测试方法中按顺序组织:
- 初始化 latch:`CountDownLatch startLatch = new CountDownLatch(1);`(所有线程等它变 0 才开始)
- 启动 N 个线程,各自先 await():每个线程调用 `startLatch.await()` 暂停,直到主线程触发释放
- 主线程调用 countDown():一旦执行,所有阻塞线程瞬间解除,真正并发执行后续逻辑(如调用目标接口、修改共享变量)
一个典型测试示例(验证计数器线程安全)
假设有一个非线程安全的计数器类 SimpleCounter,其 increment() 方法未加锁:
public class SimpleCounter {
private int count = 0;
public void increment() { count++; }
public int getCount() { return count; }
}
对应测试可写为:
@Test
public void testIncrementConcurrency() throws InterruptedException {
SimpleCounter counter = new SimpleCounter();
int threadCount = 100;
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch endLatch = new CountDownLatch(threadCount);
for (int i = 0; i {
try {
startLatch.await(); // 所有线程在此处等待
counter.increment(); // 同时执行非原子操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endLatch.countDown();
}
}).start();
}
Thread.sleep(100); // 确保所有线程已进入 await 状态
startLatch.countDown(); // ✅ 关键:统一释放
endLatch.await(); // 等待全部完成
// 预期应为 100,但很可能小于 100(说明存在竞态)
assertEquals(100, counter.getCount());
}
注意事项与增强建议
真实压测需更严谨,注意以下几点:
- 避免“假并发”:确保 `startLatch.await()` 在业务逻辑前,且所有线程都已启动并阻塞;可用 `Thread.activeCount()` 或日志辅助验证
- 补充异常捕获:线程内抛出未检查异常会静默失败,建议用 `Thread.setDefaultUncaughtExceptionHandler` 记录问题
- 配合 AtomicLong / CyclicBarrier 更灵活:如需统计响应时间、做多轮重复测试,CyclicBarrier 更适合循环场景;AtomicLong 可替代普通变量用于断言结果
- 慎用 assertTrue/assertEquals 在并发中:断言本身不是原子操作,若在多线程中直接 assert 共享状态,可能因读取时机引发误报;建议先 `endLatch.await()` 再统一断言最终值
不复杂但容易忽略细节,抓住“同步起点”这个核心,CountDownLatch 就能成为你验证并发行为的可靠工具。










