HashMap非线程安全,高并发下会导致数据丢失、死循环、异常等;应使用ConcurrentHashMap(推荐)、unmodifiableMap+重建、synchronizedMap(低频)等线程安全方案。

Java 中的 HashMap 本身不处理高并发读写场景,它完全不是线程安全的。在多线程环境下直接读写同一个 HashMap 实例,会导致:
- 数据丢失(如
put覆盖或丢失) - 死循环(JDK 7 中
resize()触发链表成环,CPU 100%) -
ConcurrentModificationException(迭代时被修改) - 读到脏数据、不一致状态甚至
null值异常
所以,不能靠“调优”或“小心使用”让 HashMap 支持高并发读写——它的设计就排除了这种可能。
✅ 正确做法:用真正线程安全的替代方案
1. 高并发读多写少 → 优先选 ConcurrentHashMap
-
读操作无锁,性能接近
HashMap - 写操作只锁单个桶(bin)的头节点,不阻塞其他桶
- JDK 8+ 使用
CAS + synchronized(头节点),比 JDK 7 的Segment更轻量 - 支持原子方法:
putIfAbsent、computeIfAbsent、merge,避免手动加锁和竞态
ConcurrentHashMap<string long> counter = new ConcurrentHashMap();
counter.merge("user1", 1L, Long::sum); // 线程安全计数</string>
⚠️ 注意:
- 不允许
null键或null值(否则抛NullPointerException) - 迭代器是弱一致性:不抛
ConcurrentModificationException,但可能漏掉刚插入的项,或看到过期值
2. 写极少、读极多且需强一致性 → 可考虑 Collections.unmodifiableMap + 定期重建
适用于配置类、字典表等只读场景:
Map<string string> safeMap = Collections.unmodifiableMap(new HashMap(source));</string>
配合 CopyOnWriteArrayList 思路(但 Map 没有 CopyOnWriteMap,需自行封装)
3. 简单场景、并发不高 → Collections.synchronizedMap
Map<string integer> syncMap = Collections.synchronizedMap(new HashMap());</string>
- 所有方法加
synchronized(this),本质是全表锁 - 并发一高,吞吐量断崖式下降,仅适合低频读写或过渡方案
4. 绝对不推荐:Hashtable
- 同样是全表
synchronized,API 过时(比如elements()、keys()) - 不支持现代原子操作,已不推荐用于新项目
❌ 常见错误写法(务必避免)
// 危险!组合操作非原子
if (!map.containsKey("key")) {
map.put("key", value); // 中间可能被其他线程插入,导致覆盖
}
// 正确替代(ConcurrentHashMap 提供的原子方法)
map.putIfAbsent("key", value);
// 危险!get + put 不是原子的
Integer old = map.get("count");
map.put("count", old == null ? 1 : old + 1);
// 正确替代
map.merge("count", 1, Integer::sum);
? 补充建议:提升并发性能
- 初始化时预估容量:
new ConcurrentHashMap(expectedSize),减少扩容开销 - 避免在
computeIfAbsent等回调中执行耗时操作(如远程调用、IO),会阻塞写线程 - 若需遍历+修改,不要依赖
entrySet().iterator()的强一致性;改用forEach或reduce等内置并发方法
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











