onlyit onlyifabsent( 四 )

  • 最小值:00000000 00000000 10000000 00000001
  • 可以发现最大值或者最小值高 16 位全部为 0,这个值记下,马上会用到
    接着会进行 2 个 if 判断,第一次进入这个循环的时候 sc 等于扩容阈值,是大于 0 的,会进入第二个 if,条件是通过 CAS 对sizeCtl 进行赋值,成功则进入 transfer 方法开始扩容,失败进行下一次循环,我们看下赋的值是多少,(rs << RESIZE_STAMP_SHIFT) + 2,rs 是刚刚根据容量计算得出的值,将他左移 16 位,那么低 16 位会全部变为 0,然后加 2,这里是什么意思呢
    其实开始扩容之后 sizeCtl 保存的值,高 16 位表示原集合的容量,低 16 位表示的是参与扩容的线程数,读到这里,我们可能会想,直接用 sizeCtl 保存容量,再建一个变量保存线程数不就行了吗,干嘛费这么老大一个劲
    其实这也是 JDK 的一大特性,对内存的极致使用,如果你源码读多了,就会发现很多地方都是这样玩的,高 16 位保存一个值,低 16 位保存另一个值,又省内存,逼格又高
    onlyit onlyifabsent

    文章插图

    所以 CAS 失败的线程再一次循环,因为此时的 sizeCtl 已经是负数了,所以会进入第一个 if,这里又是一波位运算的骚操作,我们一个个看,(sc >>> RESIZE_STAMP_SHIFT) != rs,将之前计算得到的 sizeCtl 值右移 16 位,因为线程数的加减只是在低 16 位进行,所以右移 16 位之后得到的值就是原集合的容量
    这里又有一个小细节,大家有没有发现这里使用的是>>>,之前赋值的时候使用的是<<,怎么多了一个箭头呢,>>>表示的是无符号右移,即右移之前如果是负数,右移之后高位补 0,会变成正数,相对应的是>>,右移之前是负数,右移之后还是负数,所以第一个条件就是判断容量是否发生了变化
    第二个和第三个条件实际上是 JDK8 的一个 bug,JDK12 中修复,详情参考oracle 官网,正确的条件应该是
    • sc == (rs << RESIZE_STAMP_SHIFT) + 1 || sc == (rs << RESIZE_STAMP_SHIFT) + MAX_RESIZERS
    第一个是判断扩容的线程数是否为 0,读者可能会有疑问,一开始不是+2 吗,为什么这里是+1,因为一开始第一个扩容的线程已经把自己算进去了,所以初始值应该是+1,然后自己又+1,所以就是+2 了,第二个是判断扩容线程是否达到最大线程数 MAX_RESIZERS
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;最后 2 个条件是判断扩容之后的数组是否为 null,以及扩容的下标位是否到 0 了,也就是是否扩容结束了
    (nt = nextTable) == null || transferIndex <= 0整个 if 的 5 个条件都是判断扩容是否结束了,这里其实还隐藏了个细节,后面讲到 transfer 方法的时候会讲到
    如果此时没有扩容完毕,那么通过 CAS 对 sizeCtl 进行赋值+1 操作,成功则进入 transfer 方法协助扩容,在循环的最后会进行一个集合容量的计算操作
    s = sumCount();判断是否需要再一次进行扩容,可能有读者会有疑问,会不会在扩容的过程中又触发一次扩容,这个在后面 transfer 方法讲完你就会知道了,这里简单提一下,是不会的,因为完成扩容的线程进行下一次循环执行到这个 if 判断的时候,transfer 必然等于 0,不会再次进入 transfer 方法,只有最后一个出来的线程有可能再次触发扩容操作
    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0)addCount 到这里基本说完了,我们接下来将一开始提到的 fullAddCount 方法


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