1. 问题背景:一个HashMap引发的血案
那天凌晨3点,我被刺耳的电话铃声惊醒。运维同事急促的声音从听筒传来:"订单服务完全瘫痪了,所有下单请求都卡死!"我瞬间清醒,抓起笔记本就开始远程排查。
登录服务器后,看到监控大屏一片飘红:CPU占用率100%,线程数爆表,订单服务响应时间突破天际。快速查看日志,发现大量线程卡在同一个地方:
code复制"http-nio-8080-exec-12" #32 daemon prio=5 os_prio=0 tid=0x00007f8a3c1e8000 nid=0x1e3 waiting for monitor entry [0x00007f8a2d3fe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.OrderService.decrementStock(OrderService.java:15)
- waiting to lock <0x00000000f5d1b0c0> (a java.util.HashMap)
问题锁定在OrderService的库存扣减方法。这个看似简单的逻辑,在高并发场景下露出了狰狞面目:
java复制public class OrderService {
private static Map<String, Integer> stockMap = new HashMap<>();
public void decrementStock(String productId) {
Integer stock = stockMap.get(productId);
if (stock > 0) {
stockMap.put(productId, stock - 1);
}
}
}
当时正值电商大促,每秒上千的并发请求让这个HashMap彻底崩溃。更可怕的是,由于线程无限阻塞,整个Tomcat线程池被耗尽,导致所有请求都无法处理。
关键教训:永远不要低估生产环境的并发量。开发时测试的几十个并发,和真实场景可能差几个数量级。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 深入原理:为什么HashMap会崩溃
2.1 HashMap的线程不安全本质
HashMap的线程不安全主要体现在三个方面:
-
数据竞态(Race Condition):
- 当线程A执行
get()后,线程B可能已经修改了值 - 导致线程A基于过期数据做决策
- 在我们的案例中,多个线程同时看到stock=1,都认为可以扣减,最终导致超卖
- 当线程A执行
-
无限循环风险:
- HashMap在扩容时需要进行rehash
- 多线程并发rehash可能导致链表成环
- JDK7中这个问题尤为严重,可能直接导致CPU 100%
-
可见性问题:
- 由于没有volatile修饰,一个线程
