onlyit onlyifabsent( 八 )


if (i < 0 || i >= n || i + n >= nextn) {int sc;if (finishing) {nextTable = null;table = nextTab;sizeCtl = (n << 1) - (n >>> 1);return;}if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)return;finishing = advance = true;i = n; // recheck before commit}}else if ((f = tabAt(tab, i)) == null)advance = casTabAt(tab, i, null, fwd);else if ((fh = f.hash) == MOVED)advance = true; // already processedelse {synchronized (f) {if (tabAt(tab, i) == f) {省略 。。。}}}接下来有 4 个 if 判断,第一个 if,当扩容完毕时 i 才会小于 0,第二个 if,如果目标索引位的值为 null,通过 CAS 设置为 ForwardingNode
static final class ForwardingNode<K,V> extends Node<K,V> {final Node<K,V>[] nextTable;ForwardingNode(Node<K,V>[] tab) {super(MOVED, null, null, null);this.nextTable = tab;}}ForwardingNode 是对 Node 的一个简单封装,hash 值是-1,表示正在扩容,nextTable 指向扩容之后的数组对象
第三个 if,如果发现节点 hash 值为-1,那么继续,这里,大家想过没有,什么时候会执行到这个分支呢,从上面的 while 循环可以发现,每个线程都负责有自己的扩容区间,是不会走到其他线程的区间的,那这个 if 判断是干嘛的呢?读者可以先思考一下,后面会提到

onlyit onlyifabsent

文章插图

如果节点不为空,会将节点的值转移到新的数组上去,看过 HashMap 扩容源码的话,这块理解起来就会很容易了,我们计算 key 在数组中下标位是通过 hash&(n-1)来计算的,当数组扩容后,hash 是不变的 ,变得只是 n-1,举个例子,n=8,扩容之后 n=16,转换成二进制
  • 8-1: 00000111
  • 16-1: 00001111
也就是说,key 在扩容之后数组的位置取决于比 8 高一位的那个位置和 hash 计算的结果,而那个位置是几呢,如果 n=8,那个位置就是 8
  • 8:00001000
所以,源码里直接计算 int runBit = fh & n,如果这个值为 0,那么扩容后位置不变,如果为 1,那么计算后的值正好是原索引位+n,所以,通过一个 for 循环计算链表的最后一个节点的值是 0 还是 1,这里有一个细节,如果最后几个节点计算结果一致,那么取第一个节点赋值给 lastRun,什么意思,举个例子
  • 1->2->3->4->5->6->null
如果 4,5,6,3 个节点计算的结果一致,那么 lastRun=4,然后根据 runBit 的值,给高位引用 hn 和低位引用 ln 赋值,hn 就表示位置发生变化的节点,ln 变化位置不发生变化的,然后再通过一个 for 循环,将位置发生变化和不发生变化的分成 2 个链表,最后赋值给新的数组下标位
当所有的数据都完成迁移后,i 会变成-1,来到这个 if
if (i < 0 || i >= n || i + n >= nextn) {int sc;if (finishing) {nextTable = null;table = nextTab;sizeCtl = (n << 1) - (n >>> 1);return;}if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)return;finishing = advance = true;i = n; // recheck before commit}}刚进入的时候,finishing 是 false,进入第二个 if,因为自己的扩容任务结束了,所以要把 sizeCtl 减 1,当然也是通过 CAS 的,如果失败了,重新进入循环再试一次,里面那个 if 判断应该不陌生了吧,还记得我们一开始赋值的时候吗,这个就是判断此时线程是否是最后一个执行扩容的线程,如果不是,退出方法,没你事了,否则,finishing 设为 true,i=n,看到这里会不会觉得奇怪,i 怎么又等于 n 了呢,没错,从头到尾再检查一遍,还记得一开始 while 里说的那个 finishing 条件吗,如果没有这个条件,那么最后一个线程只会检查自己边界 bound 到 n 的区间,但是有了这个条件,则会把所有区间都检查一遍


特别声明:本站内容均来自网友提供或互联网,仅供参考,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。