467 字
2 分钟
ConcurrentHashMap 的实现:分段锁到桶级并发
ConcurrentHashMap 的实现重点是降低锁竞争:JDK 1.7 通过 Segment 分段,JDK 1.8 去掉 Segment,将并发控制细化到数组桶。
JDK 1.7:分段锁
底层使用多个 Segment,每个 Segment 维护自己的桶数组并通过 ReentrantLock 协调修改,结构类似哈希表,但不是简单包装一个普通 HashMap。修改不同段时可以并行,修改同一段才有锁竞争,并发修改能力受段划分影响。

JDK 1.8:CAS + 桶级 synchronized
JDK 1.8 不再使用 Segment 分段锁。向空桶插入通常通过 CAS;向非空桶修改时通常使用 synchronized 协调当前桶,树桶等路径还有自己的机制,不是所有操作只需要一个 CAS。
普通 get 一般不获取写操作使用的监视器锁,依靠 volatile 等可见性机制和数据结构协议读取。无锁读取不等于所有操作都无锁。


常见追问
扩容期间通过转发节点等标记协调新旧数组,读取遇到已迁移桶会转到新表,写线程也可能帮助迁移。并发修改时 size 可能只是瞬时估计,不能用它做严格一致的业务条件判断。
ConcurrentHashMap 禁止 null key 和 null value,使 get 返回 null 能明确表达没有对应映射,尤其避免 null value 的歧义。containsKey 后再 put 仍不是原子步骤,应按意图使用 putIfAbsent、compute 等原子 API。
面试回答模板
JDK 7 使用 Segment 分段锁,JDK 8 主要通过空桶 CAS、非空桶同步和并发扩容协议提高并发能力。读取一般不获取写锁,但修改需要协调。它禁止 null,提供 putIfAbsent 等原子操作;遍历是弱一致的,不保证某个时刻全表完全一致的快照。
ConcurrentHashMap 的实现:分段锁到桶级并发
https://blog.xqcherry.top/posts/interview/java/concurrenthashmap-internals/ 目录