concurrenthashmap的transfer方法通过任务分片、forwardingnode标记和sizectl协调三重机制实现多线程无锁扩容:transferindex原子递减分配桶区间,fwd节点标识已迁移桶以保障读写不阻塞,sizectl负值编码扩容状态与参与线程数。

ConcurrentHashMap 的 transfer 方法通过“任务分片 + 状态协作 + 节点标记”三重机制,让多个线程安全、无锁、高效地共同完成扩容,而不是排队等待或互相阻塞。
用 transferIndex 动态划分迁移任务区域
扩容开始后,所有参与线程共享一个 volatile int transferIndex,它记录**下一个待分配的桶索引起点**。每个线程通过 CAS 原子递减该值,领取一段连续的桶区间(如 [i, bound)),避免重复处理:
- 初始
transferIndex = oldTab.length(比如 32) - 线程 A CAS 成功将它改为 16 → 领取区间 [16, 32)
- 线程 B 接着 CAS 成功改为 0 → 领取区间 [0, 16)
- 后续线程发现
transferIndex ≤ 0,说明任务已分完,转为“协助者”角色
用 ForwardingNode 标记已完成迁移的桶
当某个桶(如 tab[i])被迁移完毕,会用 CAS 将其头节点替换为 ForwardingNode(转发节点),该节点持有对新表 nextTable 的引用:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 后续任何线程对该桶的
get或put操作,看到ForwardingNode就直接跳转到nextTable[i]或nextTable[i + n]查找 - 这保证了读操作零阻塞,写操作也能立即感知迁移进度
-
ForwardingNode是线程安全的“完成信号”,无需加锁即可判断桶状态
用 sizeCtl 协调扩容生命周期和线程数
sizeCtl 是核心协调变量,它的值编码了当前扩容阶段和参与线程数:
-
sizeCtl = -(1 + N):表示有 N 个线程正在扩容(如 -3 表示 2 个线程在搬数据) - 新线程想加入扩容时,先检查
sizeCtl ,再尝试 CAS 增加计数(<code>sc + 1) - 若达到最大并发线程数(
MAX_RESIZERS)或迁移已结束(transferIndex == 0),则放弃协助 - 最后一个完成任务的线程负责清理
nextTable、更新table引用,并重置sizeCtl为新阈值
协助线程如何“帮一把”而不冲突
不是所有线程都从头抢任务。当线程在 put 中遇到 ForwardingNode,会主动调用 helpTransfer 协助:
- 它不重新申请大块区间,而是从自己要插入的 key 对应的旧桶位置开始,**逆序扫描附近未完成的桶**(如 i−1, i−2…)
- 对每个桶加
synchronized (tab[i])锁(仅锁当前桶头节点),确保迁移链表/红黑树时无竞争 - 迁移时按高位 bit 拆分:原桶中节点根据 hash 的最高位是 0 还是 1,分别放入
nextTable[i]和nextTable[i + n]
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










