从线上事故到性能优化:JMeter压测揭秘synchronizedList与CopyOnWriteArrayList的真实表现
去年双十一大促期间,我们的商品推荐服务突然出现响应延迟飙升。监控系统显示,某个核心接口的TP99从平时的50ms暴涨到800ms,更诡异的是部分用户看到的推荐列表竟然出现重复商品。经过紧急排查,最终定位到问题根源——一个被多个线程共享的ArrayList。这个看似简单的集合类型选择失误,差点让我们损失数百万销售额。本文将完整还原这次事故的诊断过程,并通过JMeter压测对比两种线程安全List的实际表现,为高并发场景下的集合选型提供数据支撑。
1. 事故现场还原与问题诊断
那天的故障从下午3点开始,正值流量高峰。运维团队最先收到报警,发现10台服务器中有3台的CPU利用率突破90%。通过Arthas实时诊断,我们很快锁定了一个名为getHotProducts()的方法。该方法内部维护了一个存放热门商品ID的ArrayList,会被多个推荐线程频繁读取和更新。
关键问题复现代码片段:
java复制// 问题代码示例
List<String> hotProductIds = new ArrayList<>();
// 线程A(更新线程)
public void updateHotProducts(List<Product> newProducts) {
hotProductIds.clear();
newProducts.forEach(p -> hotProductIds.add(p.getId()));
}
// 线程B(读取线程)
public List<String> getRecommendations() {
return hotProductIds.stream()
.limit(10)
.collect(Collectors.toList());
}
当用JConsole监控线程状态时,我们观察到了典型的多线程问题特征:
- 大量线程阻塞在
java.util.ArrayList.add()方法上 - 偶尔出现`ArrayIndexOutOfBoundsExcep
