ConcurrentHashMap 更适合高并发场景因其采用分段锁(JDK 7)或 CAS + synchronized 锁单个 Node(JDK 8+),读操作无锁、写操作细粒度加锁,吞吐量远高于全表锁的 Hashtable 或 synchronizedMap;但仅保证单方法原子性,复合操作需用 putIfAbsent、computeIfAbsent 等原子方法,且 mappingFunction 应轻量无副作用。

ConcurrentHashMap 为什么比 Hashtable 和 synchronizedMap 更适合高并发场景
因为 ConcurrentHashMap 不是对整个 map 加锁,而是采用分段锁(JDK 8+ 改为 CAS + synchronized 锁单个 Node),读操作完全无锁,写操作只锁对应桶(bucket),吞吐量远高于 Hashtable(全表锁)或 Collections.synchronizedMap(new HashMap())(所有方法都套用同一把对象锁)。
常见错误是误以为“线程安全 = 可以随便用”,结果在复合操作(如先 get 再 put)中仍出现竞态——ConcurrentHashMap 保证单个方法原子性,不保证多步逻辑原子性。
- 高频读 + 低频写:默认构造即可,
new ConcurrentHashMap() - 已知容量且写较多:预估初始容量并指定
concurrencyLevel(JDK 7)或直接用initialCapacity(JDK 8+,内部自动计算) - 避免用
size()做条件判断——它返回的是近似值;需精确计数应改用mappingCount()
putIfAbsent、computeIfAbsent 这些方法怎么选才不踩坑
它们是解决“检查后插入”这类复合操作的原子方案,但行为差异明显:
-
putIfAbsent(key, value):仅当 key 不存在时插入 固定值,value 不会重新计算 -
computeIfAbsent(key, mappingFunction):key 不存在时才调用函数生成 value,且该函数只执行一次(即使多个线程同时触发) -
merge(key, value, remappingFunction):key 存在则用函数合并新旧值,不存在则直接 put
典型误用:computeIfAbsent 的 mappingFunction 里做了 I/O 或耗时操作——一旦多个线程争抢同一个 key,只有一个线程执行函数,其余阻塞等待,可能引发线程饥饿。应确保函数轻量、无副作用。
立即学习“Java免费学习笔记(深入)”;
ConcurrentHashMap> map = new ConcurrentHashMap<>(); map.computeIfAbsent("users", k -> new ArrayList<>()).add("alice"); // 安全
遍历时用 entrySet() 还是 forEach?迭代器是否支持 remove()
ConcurrentHashMap 的迭代器是弱一致性的(weakly consistent):不抛 ConcurrentModificationException,可反映创建迭代器之后的部分修改(但不保证看到全部),也不阻塞写操作。
- 不要在遍历中调用
remove()或put()修改当前 key —— 虽不报错,但行为不可靠 - 需要边遍历边删除?用
computeIfPresent(key, (k,v) -> null)或remove(key)单独调用 - 批量处理推荐
forEach(BiConsumer)或replaceAll(BiFunction),它们内部按 segment 分批处理,性能更好
map.forEach((k, v) -> {
if (v.isEmpty()) {
map.remove(k); // ✅ 安全:单独 remove
}
});
与 HashMap 混用时的序列化和类型兼容问题
ConcurrentHashMap 实现了 Serializable,但反序列化后得到的是普通 HashMap 实例(除非显式 new),这会导致后续并发写入出错。
- 序列化前确认:必须用
ConcurrentHashMap构造反序列化对象,不能依赖默认反序列化逻辑 - 泛型擦除不影响,但注意
ConcurrentHashMap不允许 null 作为 key 或 value(会抛NullPointerException),而HashMap允许 - 若从 JSON 反序列化(如 Jackson),需注册专用模块或使用
@JsonDeserialize(as = ConcurrentHashMap.class)
最易忽略的一点:JDK 9+ 引入的 Map.ofEntries() 返回的是不可变 map,不能强转成 ConcurrentHashMap——运行时报 ClassCastException。










