接着会进行 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 位保存另一个值,又省内存,逼格又高

文章插图
所以 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
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 方法
- 陆游临安春雨初霁赏析及感悟 临安春雨初霁陆游赏析
- 不足5㎡的卫生间,也有很多需要“操心的事”,建议大家收藏!
- 孕产妇孕期保健 孕妇保健app
- game游戏中心官方 在线游戏网站
- 核舟记翻译文言文 核舟记翻译最简短
- 刀锋意志 手游刀锋意志出装
- 现代养殖业最赚钱行业 哪种养殖业最赚钱
- 火炬之光1技能树 火炬之光技能搭配
- 皇帝成长计划游戏 皇帝成长计划测试版
- 阿里巴巴国际站好做吗? 阿里巴巴国际站怎么做好
特别声明:本站内容均来自网友提供或互联网,仅供参考,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
