二 ConcurrentHashMap源码深度解析(java8)——直呼Dou( 五 )

<=0 说明任务已经迁移任务已经分配完了 。if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||transferIndex <= 0)// 处于扩容状态,且扩容已经结束 or 扩容的线程达到最大值,则没必要帮助扩容break;// 帮助扩容的线程+1if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))transfer(tab, nt);}// 2. 第一个触发扩容的线程// (rs << RESIZE_STAMP_SHIFT) + 2,为什么加2呢// 1000000000011001 0000 0000 0000 0000 + 2// sc = 1000000000011001 0000 0000 0000 0010else if (U.compareAndSwapInt(this, SIZECTL, sc,(rs << RESIZE_STAMP_SHIFT) + 2))transfer(tab, null);// 3. 第一个扩容线程没有触发成功,则重新统计元素总个数,再循环一次 。s = sumCount();}}}
刚才计算的元素总个数s >= sc扩容的阈值,并且tab的地址没有改变,并且数组的长度没有达到最大值则触发扩容 。
(1)计算过程
rs是什么意思,有什么作用?刚开始就被一块石头绊住了 。那就来先看看(n)的计算过程:
/*** 返回值作为正在扩容的数据表的size即n的一个标志,rs可以反推出n* Returns the stamp bits for resizing a table of size n.* Must be negative when shifted left by RESIZE_STAMP_SHIFT.**/static final int resizeStamp(int n) {return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));}
看源码注释,返回值作为正在扩容数组的长度n的一个标志位?并且当向左移=16位时得到一个负数?
.(n)的作用是获取n的二进制从左往右连续的0的个数,比如:
2的二进制10从左往右有30个连续的04的二进制100从左往右有29个连续的08的二进制1000从左往右有28个连续的016的二进制10000从左往右有27个连续的0(int有32位,左边不足的补0)
(1 >> ) != rs判断当前数组是否在扩容状态,也是可以判断扩容是否结束的,扩容真的结束了,sc会修改为新数组的扩容阈值,自然就不处于扩容状态了,只是会使得一些线程参与到了扩容中却发现结束了(逻辑虽然不严谨,但是对项目运行性能的影响也不大) 。
既然是bug,后面的版本应该会修复这个bug吧,找到还没修复,找到看样子修复了:
(3)元素迁移
不管第一个扩容线程还是帮助线程都会调用,顾名思义,转移,从旧数组转移到新数组的过程 。这个过程中涉及到扩容线程任务的分配和元素的复制迁移 。(吐槽一下官方源码,一个函数代码长就算了,if-else还很多,在实际开发中,讲究一个去else化,能及时返回就及时返回,搞这么多else分支,还互相嵌套,可读性很差诶 。)
注释很清楚,虽然长,情况又多,反复琢磨几遍,明白作者的意图,就觉得还行,是人的思维 。
private final void transfer(Node[] tab, Node[] nextTab) {int n = tab.length, stride;// 单核不拆分,下面讨论多核的情况// 计算步长,拆分任务n >>> 3 = n / 2^3// 先将n分为8份,然后等分给每个cpu,若最后计算的步长小于最小步长16,则设置为16if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)stride = MIN_TRANSFER_STRIDE; // subdivide rangeif (nextTab == null) {// initiatingtry {// 扩容 2倍@SuppressWarnings("unchecked")Node[] nt = (Node[])new Node[n << 1];nextTab = nt;} catch (Throwable ex) {// try to cope with OOMEsizeCtl = Integer.MAX_VALUE;return;}nextTable = nextTab;// transferIndex 记录迁移进度transferIndex = n;}int nextn = nextTab.length;ForwardingNode fwd = new ForwardingNode(nextTab);// 从后面的迁移逻辑看到 迁移复制元素是逆序迁移// advance= true 则代表可继续向前一个位置迁移复制元素boolean advance = true;// 是否所有线程都全部迁移完毕,true则可以将nextTab赋值给table了boolean finishing = false; // to ensure sweep before committing nextTab// i 代表当前线程正在迁移的数组位置,bound代表它本次可以迁移的范围下限for (int i = 0, bound = 0;;) {Node