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

<< 1];for (int i = 0; i < n; ++i)rs[i] = as[i];counterCells = rs;}} finally {cellsBusy = 0;}collide = false;continue;// Retry with expanded table}// (5)重新生成一个伪随机数赋值给h,进行下一次循环判断// 再哈希h = ThreadLocalRandom.advanceProbe(h);}// 2.as 为null or as是空的else if (cellsBusy == 0 && counterCells == as &&U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {boolean init = false;try {// Initialize table// 多次判断counterCells == as,未防止as变更if (counterCells == as) {// 初始化CounterCell数组,初始容量为2CounterCell[] rs = new CounterCell[2];rs[h & 1] = new CounterCell(x);counterCells = rs;init = true;}} finally {cellsBusy = 0;}if (init)break;}// 3. 2中修改CELLSBUSY失败没抢到初始化as的锁,则尝试 直接cas修改baseCount + xelse if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))break;// Fall back on using base}}
2、
如何统计所有元素个数呢?基数+[]之和 。
final long sumCount() {CounterCell[] as = counterCells; CounterCell a;long sum = baseCount;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)sum += a.value;}}return sum;}
像提供给用户获取元素个数的方法size()以及判空的()都是调用了() 。
public int size() {long n = sumCount();return ((n < 0L) ? 0 :(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int)n);}public boolean isEmpty() {return sumCount() <= 0L;}
四、扩容
扩容是的核心,明白作者的意图就不觉得难了 。扩容机制的触发有三个地方:
1、触发扩容
除去链表转红黑树可能触发的扩容,算是最正统的扩容源头,所以首先从开始探寻扩容的神秘足迹 。
private final void addCount(long x, int check) {CounterCell[] as; long b, s;// 更新元素个数,详细解析看 三、if ((as = counterCells) != null ||!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {CounterCell a; long v; int m;boolean uncontended = true;// ThreadLocalRandom.getProbe() 相当于当前线程的hash值if (as == null || (m = as.length - 1) < 0 ||(a = as[ThreadLocalRandom.getProbe() & m]) == null ||!(uncontended =U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {// 找到对应的格子不为null,则cas 该格子内的value+x// counterCells为空or对应格子为空or update格子失败uncontended=false,// 则进入fullAddCount,这个方法是一定会加成功的,但是因为这个过程可能会比较耗时,加成功就立刻退出整个方法了,fullAddCount(x, uncontended);return;}// 从put走到addCount,check是一定>=2的,// 从computeIfAbsent到addCount,可能check =1,意为没有发生哈希冲突的添加元素,则不会检查扩容 。// 毕竟扩容是个耗时的操作if (check <= 1)return;// 统计下元素总个数 。s = sumCount();}// 扩容重点看这里// 替换节点和清空数组时,check=-1,只做元素个数递减,不会触发扩容检查,也不会缩容 。if (check >= 0) {Node[] tab, nt; int n, sc;while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&(n = tab.length) < MAXIMUM_CAPACITY) {// s 元素个数 >= sc扩容的阈值,并且tab的地址没有改变,并且数组的长度没有达到最大值// 则开始扩容// 以n=64举例// rs=32793 1000000000011001int rs = resizeStamp(n);if (sc < 0) {// 1. 扩容检查,若需要帮助,则帮助扩容// ①(sc >>> RESIZE_STAMP_SHIFT) != rs 为了判断是否处于扩容状态 。// ②sc=rs+1判断扩容已经结束 百度的// ③sc==rs+MAX_RESIZERS扩容线程数超过最大值 百度的// sc < 0 了,rs是一个正数,rs+1和rs + MAX_RESIZERS怎么可能等于一个负数?// 所以这里是一个bug,和朋友讨论,这里的确是一个bug 。// transferIndex 记录是扩容迁移元素的索引,逆序扩容,transferIndex