JDK 1.8 中 ConcurrentHashMap 改用 CAS + synchronized,实现桶级锁、多线程扩容、无锁读、红黑树优化及分段计数,显著提升高并发性能。

ConcurrentHashMap 在 JDK 1.8 中取消 Segment 分段锁,改用 CAS + synchronized 组合机制,不是简单替换,而是围绕“减少锁竞争、提升无锁路径比例、动态适配并发度”做的系统性优化。性能提升主要体现在高并发读写、扩容吞吐、内存占用和查询效率四个维度。
锁粒度从“段级”降到“桶级”,显著降低线程争抢
在 JDK 1.7 中,16 个 Segment 是固定划分,哪怕实际只有 2 个线程写入不同 key,只要落在同一 Segment 就必须排队;而 JDK 1.8 的锁对象是数组某个桶(table[i])的首节点(Node 或 TreeNode),真正实现“谁动哪桶,谁锁哪桶”。
- 多个线程往不同 hash 桶插入数据时,全程走 CAS,完全不加锁
- 仅当 CAS 失败且桶非空(说明发生哈希冲突)时,才对首节点加 synchronized 锁
- 锁对象是具体 Node 实例,不是 this 或全局对象,不会阻塞其他桶的操作
扩容支持多线程协作,避免单点瓶颈与阻塞等待
JDK 1.7 的扩容由单个线程完成,且只在当前 Segment 内进行,其他 Segment 虽可读写,但整体扩容进度慢、无法利用多核;JDK 1.8 引入协助扩容机制,让参与 put 的线程在发现正在扩容时主动帮忙迁移数据。
- 扩容时使用
ForwardingNode占位,标识该桶已迁移或正在迁移 - 后续线程访问到 ForwardingNode,会自动加入扩容队列,分摊迁移任务
- 采用尾插法迁移链表,彻底规避 JDK 1.7 头插法导致的链表反转与死循环风险
读操作彻底无锁,volatile + final 保障可见性与安全性
get 操作在 JDK 1.8 中完全不加锁,依赖 Node 字段的 volatile 修饰(val、next)和 final 语义(hash、key)保证线程间最新值可见、结构不可变。
- 即使某个桶正在被其他线程加锁修改,get 仍可安全遍历当前快照状态
- 红黑树查找也只需读取根节点,无需锁整个树,更不影响其他桶
- 相比 JDK 1.7 中 get 需要先尝试获取 Segment 锁(虽常可跳过,但逻辑复杂),JDK 1.8 更轻量、更确定
结构精简 + 红黑树优化,兼顾空间与时间效率
去掉 Segment 层后,内存模型更扁平:只有 Node[] table 一层主干,每个桶内按需切换链表/红黑树。
- 链表长度 ≥ 8 且 table.length ≥ 64 时转红黑树,将最坏查询从 O(n) 降至 O(log n)
- TreeNode 复用 Node 字段,通过类型判断区分,避免额外对象开销
- size() 使用
CounterCell[]分段累加,配合 CAS 更新,比 JDK 1.7 遍历所有 Segment 加锁统计更高效、更精确


















