ConcurrentHashMap的putVal方法根据节点状态动态决定是否加锁,能不锁就不锁,必须锁时仅锁单个桶头节点;通过CAS实现无锁插入空桶,冲突时用synchronized锁桶首节点,扩容时协助迁移或转发至新表,树化/退化也只锁根节点。

ConcurrentHashMap 的 putVal 方法并不是“在无锁和加锁状态下切换写入”,而是**根据节点状态动态决定是否需要加锁**,核心是“**能不锁就不锁,必须锁时才锁最小粒度的桶(bin)**”。它没有全局锁,也不对整个 map 加锁,而是基于 CAS + synchronized(仅作用于单个链表头或红黑树根节点)实现高效并发写入。
无锁路径:CAS 尝试直接插入到空桶
当目标桶(table[i])为 null 时,putVal 会用 CAS(如 U.compareAndSetObject(tab, i, null, newNode))尝试把新节点设为该桶的首节点。成功则写入完成,全程无锁;失败说明其他线程已抢先写入,此时进入下一轮检查或退化为加锁流程。
加锁路径:仅对冲突桶的头节点加 synchronized
当桶非空(已有节点),且当前线程 CAS 失败或桶是普通链表/红黑树节点时,会调用 sync 块,但锁对象是该桶的**首节点(f)本身**:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 不是锁 table 数组,不是锁整个 map,更不是锁 key 或 value
- 锁的是
f—— 即tab[i]当前引用的那个 Node 对象(可能为链表头或树根) - 多个线程往不同桶写,锁对象不同,完全不互斥;同一桶的写操作才串行化
特殊状态处理:扩容时的协助与转发
如果当前桶是 ForwardingNode(表示正在扩容),putVal 不加锁,而是协助扩容或把写请求转发到新表:
立即学习“Java免费学习笔记(深入)”;
- 调用
helpTransfer帮忙迁移部分桶 - 或直接在新表(nextTable)对应位置重试
putVal - 这个过程仍是无锁的(依赖 CAS 和 volatile 变量协调)
树化与退化中的锁范围
当链表转红黑树(treeifyBin)或树转链表(untreeify)时,同样只对桶首节点加锁:
- 树化前先获取桶头锁,再检查长度、转换结构
- 即使树很大,锁也只作用于根节点对象,不影响其他桶,也不阻塞读操作(get 不加锁)

















