transfer是ConcurrentHashMap扩容的核心方法,负责将旧数组节点迁移至新数组;它由put触发扩容或线程访问到ForwardingNode时自动协助执行,不对外暴露。

ConcurrentHashMap 在 JDK 8 中采用分段锁 + CAS + volatile 的设计,其扩容过程(即 transfer 方法)是并发、渐进、无锁化的。它不靠“协助线程”主动调用某个接口来参与迁移,而是当线程在 put/get 等操作中**发现正在扩容(即 tab != nextTab,且 nextTab 不为 null)时,自动帮助迁移一部分桶(bin)**。
什么是 transfer?它怎么被触发?
transfer 是 ConcurrentHashMap 扩容的核心方法,负责把旧数组(tab)中的节点逐个迁移到新数组(nextTab)。它本身不对外暴露,也不由用户直接调用。触发时机有两个:
- 当前线程执行
put时发现 sizeCtl - 当前线程是触发扩容的“首发起线程”,会先初始化
nextTab,再调用transfer(tab, nextTab)启动迁移。
线程如何“协助”迁移?关键看 helpTransfer
真正体现“协助”的逻辑在 helpTransfer 方法中。例如 putVal 在发现头节点是 ForwardingNode(标记该桶已迁移或正在迁移)时,会调用它:
- 检查当前
nextTab是否有效(未被取消或已完成); - 若有效,当前线程就加入迁移工作:从
transferIndex原子递减获取一个待迁移的桶区间(如从 15→12),然后调用transfer(tab, nextTab)迁移对应范围; - 每个协助线程只负责一段连续的桶(默认每轮处理 16 个桶),避免竞争,也防止某一线程长期阻塞。
迁移过程做了什么?不是简单复制
对每个待迁移的桶,transfer 会:
立即学习“Java免费学习笔记(深入)”;
- 加锁(synchronized on the first node)确保单线程处理该桶链表或红黑树;
- 按节点 hash 的第 n 位(n = oldCap 的最高位)将节点拆分为两组:低位组(留在原索引)、高位组(放入 i + oldCap);
- 链表用两个指针(loHead/hiHead)分别串起两组,树结构则根据数量决定是否退化为链表或重建树;
- 迁移完成后,在旧数组对应位置插入
ForwardingNode,表示“此桶已完成迁移”。
为什么能保证线程安全?关键机制
整个协助迁移不依赖全局锁,靠的是:
-
volatile 数组引用:
table和nextTable都是 volatile 的,确保所有线程看到最新视图; -
ForwardingNode 标记:既是迁移信号,又是读操作的“跳转指令”(get 时遇到它会去
nextTab查); - CAS 控制 transferIndex:多个线程通过 CAS 争抢迁移区间,自然实现负载分摊;
- 细粒度同步:只锁单个桶的头节点,不影响其他桶的读写。
不复杂但容易忽略:协助迁移不是“义务劳动”,而是一种协作式扩容策略——谁碰到迁移中的桶,谁就顺手搬几块砖,最终让扩容在高并发下也能平滑完成。


















