cas在concurrenthashmap中并非替代加锁,而是与synchronized协同工作:无竞争时用cas快速完成初始化数组、空桶插入、basecount更新等轻量操作;有竞争或涉及链表尾插、树化、扩容迁移等复杂操作时,才退至桶级synchronized细粒度锁,从而兼顾高性能与线程安全。

CAS 在 ConcurrentHashMap 中不是“替代加锁”,而是和 synchronized 协同工作:无竞争时用 CAS 快速完成,有竞争时才退到细粒度锁。它真正替代的是传统粗粒度锁(比如 HashTable 的全局锁、JDK7 的 Segment 锁),大幅降低锁开销和阻塞概率。
CAS 主要承担这些轻量级原子操作
在 JDK8+ 的 ConcurrentHashMap 中,CAS 不是万能的“锁替身”,而是被精准用于那些可以单次判断+更新、且失败后重试代价低的操作:
- 初始化数组 table:首次 put 时通过 CAS 设置 volatile Node[] table,避免多个线程重复初始化
- 插入链表头节点:当桶为空时,直接 CAS 将新 Node 写入数组对应位置(如 tab[i] = newNode)
- 更新 size 控制变量 baseCount:计数器更新不依赖锁,靠 CAS + 自旋保证统计准确性
- 设置扩容标记 nextTable 或 transferIndex:多线程协作扩容时,用 CAS 协调任务分片,避免串行争抢
什么时候会退回到 synchronized?
CAS 只适合“读-改-写”路径短、冲突少的场景。一旦涉及复杂结构变更,ConcurrentHashMap 就会在局部加锁,但粒度极小:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 向**非空链表尾部插入**:先 CAS 头节点失败后,再对首节点加 synchronized,遍历链表找插入点
- **树化或反树化操作**:红黑树结构调整必须加锁(TreeBin 内部用 synchronized 控制)
- **正在扩容的桶迁移**:当前桶被其他线程标记为“正在转移”,后续线程直接协助迁移,不抢锁也不 CAS
为什么这种混合策略比纯锁或纯 CAS 更合理?
它把并发控制拆解成不同层级,按需选用最经济的同步手段:
- CAS 用于“快路径”——多数情况一次成功,零阻塞、零上下文切换
- synchronized 用于“慢路径”——只锁定一个 Node 或 TreeBin,不影响其他桶的读写
- 扩容全程无全局锁,靠 CAS 更新 transferIndex + 线程协作,吞吐随 CPU 核数线性增长
对比旧版本,CAS 带来的实质改进
不是简单“去掉锁”,而是重构了竞争模型:
- JDK7 分段锁:16 个 Segment,每个是一把 ReentrantLock,哪怕只改一个 key,也要锁住整个段(约 1/16 的数据)
- JDK8 CAS + synchronized:锁只落在发生实际冲突的那个数组槽位(Node),并发度≈数组长度,且读操作完全无锁
- 结果是:高读低写场景下几乎无锁开销;中高写场景下锁争抢大幅减少,CPU 利用率更平稳
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










