ConcurrentHashMap锁机制演进本质是持续缩小锁粒度、减少竞争、降低开销:JDK1.7采用分段锁(Segment),默认16段,每段独立加ReentrantLock;JDK1.8废弃Segment,改用CAS+桶级synchronized,锁粒度细化至单个Node,并引入红黑树与多线程扩容。

ConcurrentHashMap 的锁机制演进,本质是不断缩小锁粒度、减少竞争、降低开销的过程。JDK 1.7 的分段锁是重要过渡,JDK 1.8 则用 CAS + 桶级 synchronized 彻底重构,不再有 Segment。
分段锁(JDK 1.7):把锁“切片”
分段锁不是给整个 Map 加一把锁,而是把数据分成多个逻辑段(默认 16 个 Segment),每个 Segment 是一个继承 ReentrantLock 的独立锁对象,内部维护自己的 HashEntry 数组和扩容阈值。
- 线程操作前先哈希定位到具体 Segment,只锁定该段,其他段可并发访问
- 写操作(如 put)需获取对应 Segment 的锁;读操作在大多数情况下无需加锁,靠 volatile 和 UNSAFE 保证可见性
- 并发度上限由 Segment 数量决定,默认 16,无法动态调整
JDK 1.8 彻底移除 Segment:锁粒度降到桶级别
1.8 版本完全废弃 Segment 结构,核心数组变为 Node
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 插入时,若桶为空,优先用 CAS 设置新节点;若桶非空,则对头节点加 synchronized 锁(锁的是 Node 对象本身)
- 链表长度 ≥ 8 且数组长度 ≥ 64 时转为红黑树,避免长链表带来的串行化瓶颈
- 扩容采用多线程协作迁移,通过 sizeCtl 状态变量协调,避免单线程阻塞整个 Map
CAS 在 ConcurrentHashMap 中的关键作用
CAS 不是替代锁,而是与锁协同的无锁优化手段,主要用在低竞争场景:
立即学习“Java免费学习笔记(深入)”;
- 初始化 table:多个线程同时调用 put,仅一个能通过 CAS 将 sizeCtl 设为 -1,获得初始化权限
- 插入首个节点:桶为空时,直接 CAS 设置新 Node,避免进入同步块
- 更新计数器:size() 方法不直接累加,而是通过 baseCount + CounterCell 数组 CAS 更新,减少竞争
为什么放弃分段锁?不只是“更细”,更是结构简化
分段锁的问题不止于粒度偏粗,还带来结构性负担:
- 每个 Segment 都是一套完整子哈希表,含冗余元数据(threshold、count 等),内存占用高
- 扩容需逐个 Segment 扩容,逻辑复杂且难以并行
- Segment 数量固定,无法随负载动态伸缩,并发能力僵化
- 无法引入红黑树优化查询,链表过长时性能明显下降

















