onlyit onlyifabsent( 三 )


文章插图

理解了这点,我们在看代码就轻松多了
第一次进入这个方法,counterCells 默认是为 null 的,所以会通过 CAS 尝试对 baseCount 进行+1 操作,如果成功跳出 if 进行下一个判断,失败则说明存在并发竞争,此时需要对 CounterCell 数组进行操作,进入 if 内部,首先又会进行一个 if 判断,总共 4 个条件,前 2 个条件是判断 CounterCell 数组是否为空,第三个条件是根据线程的 probe 值计算出数组的下标,然后判断这个下标是否为 null,简单说下 ThreadLocalRandom.getProbe()这个方法
static final int getProbe() {return UNSAFE.getInt(Thread.currentThread(), PROBE);}

onlyit onlyifabsent

文章插图

/** Probe hash value; nonzero if threadLocalRandomSeed initialized */@sun.misc.Contended("tlr")int threadLocalRandomProbe;这个方法的意思是获得线程的一个唯一身份码,这里我们又看到刚刚提及的那个注解sun.misc.Contended了,这个注解其实在 JDK 源码里用的还是挺多的
最后一个条件是通过 CAS 对刚刚得到的数组下标位的 CounterCell 进行赋值,如果成功则跳出 if,失败进入 if 内部
这里,不知道读者有没有注意到一个细节,外层的 CAS 和里面的 CAS 是不会连续执行的,作者在这里巧妙的运用了逻辑短路
如果 CounterCell 数组为 null,那么先尝试对 baseCount 进行操作,如果成功皆大欢喜,失败则说明存在并发竞争了,需要对 CounterCell 数组进行初始化了,里层的 if 第一个条件就是判断 CounterCell 数组是否为空,所以不会执行后面的 CAS 操作 。如果 CounterCell 数组不为空,那么说明已经存在并发竞争了,这个时候在对 baseCount 进行 CAS 操作很大程度上会失败,不如直接对 CounterCell 数组进行操作,成功率更高,所以会直接进入 if 内部,不会执行后面 CAS
进入内部 if 判断时,因为此时 CounterCell 数组不为 null,所以前 2 个条件跳过,第三个条件如果下标位为 null 则进入 fullAddCount 方法进行这个下标位的初始化,不为 null 会尝试对这个下标位进行 CAS 赋值操作,成功跳出 if,失败也进入 fullAddCount 方法 。
fullAddCount 方法后面会讲,这里暂表不提,不过这里阿轩有一个问题一直没有想明白,就是第一个 if 里有 2 个 return,为什么这里要进行 return 呢?如果有读者知道,还请在评论区解答一下,非常感谢!
继续看第二个 if 判断 ②,while 循环里的 s 是上面计算得到的集合容量,sizeCtl 是扩容阈值,所以这里就是判断集合是否需要扩容了
我们先看下resizeStamp这个方法
static final int resizeStamp(int n) {return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));}前半部分是将 n 转为二进制,计算从左到右有多少个 0,举个例子,n=16
onlyit onlyifabsent

文章插图

从左往右数总共有 27 个 0,那么 Integer.numberOfLeadingZeros(n)计算后的值就是 27,所以 Integer.numberOfLeadingZeros(n)的值介于 1-32 之间,转换成二进制
  • 最大值:00000000 00000000 00000000 00100000
  • 最小值:00000000 00000000 00000000 00000001
private static int RESIZE_STAMP_BITS = 16;RESIZE_STAMP_BITS=16,后半部分意思是将 1 左移 15 位
  • 00000000 00000000 10000000 00000000
然后进行或运算,相当于两者之和,那么 resizeStamp 的最大值和最小值分别是