countdownlatch在并发测试中用于精准模拟高并发并统计性能指标,通过startlatch实现多线程统一启动、donelatch确保主线程等待全部请求完成后再汇总结果。

CountDownLatch 在并发测试中最典型的应用,是模拟真实高并发场景并准确统计性能指标,比如接口平均响应时间、吞吐量或错误率。它解决的核心问题是:如何让多个线程真正“同时”发起请求,而不是先后串行启动;以及如何让主线程精准等待全部请求完成后再做汇总分析。
确保多线程真正并发启动
单纯用 new Thread().start() 无法保证并发——线程创建和调度存在时序差。CountDownLatch 可以实现“统一发令枪”效果:
- 主线程先创建一个
CountDownLatch startLatch = new CountDownLatch(1),作为起跑信号 - 每个工作线程启动后立即调用
startLatch.await(),全部阻塞在同一起跑线 - 主线程在启动所有线程后,只调用一次
startLatch.countDown(),瞬间释放全部线程 - 这样所有线程几乎在同一毫秒级时间点开始执行请求,逼近真实压测场景
等待全部请求完成再统计结果
主线程需等所有并发请求返回后,才能计算平均耗时、成功率等指标。这时使用第二个 CountDownLatch:
- 初始化
CountDownLatch doneLatch = new CountDownLatch(threadCount) - 每个工作线程在请求结束(无论成功或失败)后,必须执行
doneLatch.countDown() - 主线程调用
doneLatch.await()或带超时的await(60, TimeUnit.SECONDS),避免因个别请求卡死导致统计挂起 - 超时后可主动中断未完成线程,并记录超时数,提升测试鲁棒性
组合使用两个Latch实现完整压测流程
一个典型的并发测试片段如下:
int threadCount = 100;
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch doneLatch = new CountDownLatch(threadCount);
<p>List<long> durations = Collections.synchronizedList(new ArrayList());</long></p><p>for (int i = 0; i {
try {
startLatch.await(); // 等待统一指令
long start = System.nanoTime();
// 发起HTTP请求或调用目标方法
doHttpRequest();
long end = System.nanoTime();
durations.add((end - start) / 1_000_000); // 毫秒
} catch (Exception e) {
// 记录异常,仍需countDown保证统计完整性
} finally {
doneLatch.countDown();
}
}).start();
}</p><p>// 所有线程就位后,一声令下
startLatch.countDown();</p><p>// 主线程等待全部完成,最多60秒
doneLatch.await(60, TimeUnit.SECONDS);</p><p>// 统计结果
double avg = durations.stream().mapToLong(Long::longValue).average().orElse(0.0);
System.out.println("平均响应时间:" + avg + " ms");
</p>
注意避免常见陷阱
实际写测试代码时,这几个细节容易出错:
-
每个线程必须且只能调用一次
countDown()—— 多次调用会导致计数器提前归零,主线程过早退出,漏统计部分结果 -
务必在
finally块中调用countDown()—— 即使请求抛异常或超时,也要确保计数器被减,否则await()会永久阻塞 -
不要复用已触发归零的 CountDownLatch —— 它是一次性的,下次测试需新建实例;如需重复使用,考虑
CyclicBarrier -
避免在主线程中混用
join()和await()—— 功能重叠且语义混乱,专注用 CountDownLatch 统一协调











