这是我参与「第四届青训营 」笔记创作活动的第4天
ConcurrentHashMap的扩容
原理简述
多线程并发扩容
扩容的时机
- addCount()方法,元素个数达到扩容阈值触发扩容。
- helpTransfer()方法,在多线程遇到当前桶节点为MOVE状态,帮助扩容
源码
- 多线程安排扩容
这个方法主要的作用就是自旋判断是否需要帮助扩容或者扩容已经结束,并记录并发线程数量
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
//当前节点为ForwardingNode,这个ForwardingNode包含了一个nextTab的下一个数组属性
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
//经典扩容两倍
int rs = resizeStamp(tab.length);
//注意这个While,while转移完,判断新数组是否需要扩容
//nextTab == nextTable这个条件说明:nextTable在扩容结束后会赋null
//table == tab说明:table在扩容结束后会赋为nextTable
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
//判断扩容是否已经结束
//transferIndex会在transfer方法中修改
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || transferIndex <= 0)
break;
//这个CAS对操作的桶加锁了,同时SIZECTL代表了正在扩容的线程数量
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
- 扩容
嫖博客一张图,详细学习源码可见,这篇博客写的是真透彻
重右移操作就可以发现是从右往左更新,transferIndex个步长
关键技术点
- ForwardingNode的作用
- 占位作用,用于标识数组该位置的桶已经迁移完毕,处于扩容中的状态。
- 作为一个转发的作用,扩容期间如果遇到查询操作,遇到转发节点,会把该查询操作转发到新的数组上去,不会阻塞查询操作。这里可以看一下get方法源码会调用node.find()这个方法在TreeNode,TreeBin,ForwardingNode都有不同的实现。如果是get正在迁移时则依旧访问旧桶,复制桶时的高低链是复制生成的,而不是原链拆过去
//这里就说明了搜索的数组是新数组
tab = ((ForwardingNode<K,V>)e).nextTable;
- 插入形式
遍历整条链找出lastRun节点,即依次划分高低链,从lastRun节点到末尾节点都属于同一个高位或低位。至于这个lastRun的作用可能就是减少操作次数吧
再嫖张图blog.csdn.net/lxsxkf/arti…