Java 8 ConcurrentHashMap 彻底弃用分段锁,采用桶级synchronized锁+CAS协同机制:锁粒度降至单桶节点级,synchronized替代ReentrantLock以降低内存开销,CAS处理无竞争操作,读操作全程无锁,扩容支持多线程协作迁移。

Java 8 中 ConcurrentHashMap 的锁机制不是“小修小补”,而是结构性重构:它彻底抛弃了 1.7 的分段锁(Segment),转向更轻量、更精细的“桶级锁 + CAS”协同模型。核心优化围绕三点展开——锁粒度变细、锁实现更轻、并发控制更智能。
锁粒度从 Segment 级降到单桶节点级
1.7 版本中,整个哈希表被划分为 16 个 Segment,每个 Segment 是一个带 ReentrantLock 的小型 HashMap。线程操作某 key 时,先两次哈希定位到 Segment,再锁住整个 Segment —— 即使只改一个桶,同 Segment 内其他桶也得排队。
1.8 彻底取消 Segment,底层是 Node[] 数组,每个数组槽位(即“桶”)对应一个链表头节点或红黑树根节点。写操作(如 putVal)只对目标桶的首节点加 synchronized,不同桶之间完全无锁竞争。理论最大并发写线程数从 16 提升至数组长度(默认 16,可扩容)。
锁实现从 ReentrantLock 切换为 synchronized
这一变化看似倒退,实则是 JVM 演进驱动的升级:
立即学习“Java免费学习笔记(深入)”;
- JVM 1.6+ 对 synchronized 做了深度优化:偏向锁(单线程场景免开销)、轻量级锁(CAS 快速尝试)、自适应自旋(避免盲目阻塞)
- synchronized 锁信息存在对象头里,不额外占内存;而每个 Segment 都要持有一个 ReentrantLock 实例,含等待队列、状态字段等,内存和初始化成本更高
- synchronized 语法简洁,JVM 自动管理加锁/解锁,规避了 lock()/unlock() 配对遗漏导致死锁的风险
CAS 与 synchronized 协同分工
1.8 不是“全靠锁”,而是按操作类型智能选型:
- CAS 用于无竞争场景:数组初始化(compareAndSet table)、sizeCtl 状态变更(如标记扩容中)、baseCount 计数更新等,全程无锁,靠原子指令完成
- synchronized 用于有竞争的临界区:真正往链表/红黑树插入节点、修改 next 指针、树化/退化等结构变更操作,必须保证可见性与互斥性
- 读操作(get)全程无锁,依赖 volatile Node.next 和 final 字段语义保证内存可见性
扩容机制同步升级:多线程协助迁移
1.7 扩容是单线程独占 Segment,其余线程阻塞等待;1.8 改为“化整为零”式协作扩容:
- 由首个触发扩容的线程初始化 nextTable,并设置 sizeCtl 为负值(如 -2 表示有 1 个线程在扩容)
- 后续线程检测到扩容中,主动协助迁移部分桶(transferIndex 控制分片),而非干等
- 迁移时对每个桶加锁,但仅锁当前正在处理的桶,不影响其他桶读写


















