ConcurrentHashMap源码剖析 (7)

ConcurrentHashMap源码剖析

2.5 集合长度的累计方式 2.5.1 addCount方法

① CounterCell数组不为空,优先利用数组中的CounterCell记录数量

② 如果数组为空,尝试对baseCount进行累加,失败后,会执行fullAddCount逻辑

③ 如果是添加元素操作,会继续判断是否需要扩容

private final void addCount(long x, int check) { CounterCell[] as; long b, s; //当CounterCell数组不为空,则优先利用数组中的CounterCell记录数量 //或者当baseCount的累加操作失败,会利用数组中的CounterCell记录数量 if ((as = counterCells) != null || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { CounterCell a; long v; int m; //标识是否有多线程竞争 boolean uncontended = true; //当as数组为空 //或者当as长度为0 //或者当前线程对应的as数组桶位的元素为空 //或者当前线程对应的as数组桶位不为空,但是累加失败 if (as == null || (m = as.length - 1) < 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) { //以上任何一种情况成立,都会进入该方法,传入的uncontended是false fullAddCount(x, uncontended); return; } if (check <= 1) return; //计算元素个数 s = sumCount(); } if (check >= 0) { Node<K,V>[] tab, nt; int n, sc; //当元素个数达到扩容阈值 //并且数组不为空 //并且数组长度小于限定的最大值 //满足以上所有条件,执行扩容 while (s >= (long)(sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) { //这个是一个很大的正数 int rs = resizeStamp(n); //sc小于0,说明有线程正在扩容,那么会协助扩容 if (sc < 0) { //扩容结束或者扩容线程数达到最大值或者扩容后的数组为null或者没有更多的桶位需要转移,结束操作 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; //扩容线程加1,成功后,进行协助扩容操作 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) //协助扩容,newTable不为null transfer(tab, nt); } //没有其他线程在进行扩容,达到扩容阈值后,给sizeCtl赋了一个很大的负数 //1+1=2 --》 代表此时有一个线程在扩容 //rs << RESIZE_STAMP_SHIFT)是一个很大的负数 else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) //扩容,newTable为null transfer(tab, null); s = sumCount(); } } } 2.5.2 fullAddCount方法

① 当CounterCell数组不为空,优先对CounterCell数组中的CounterCell的value累加

② 当CounterCell数组为空,会去创建CounterCell数组,默认长度为2,并对数组中的CounterCell的value累加

③ 当数组为空,并且此时有别的线程正在创建数组,那么尝试对baseCount做累加,成功即返回,否则自旋

private final void fullAddCount(long x, boolean wasUncontended) { int h; //获取当前线程的hash值 if ((h = ThreadLocalRandom.getProbe()) == 0) { ThreadLocalRandom.localInit(); // force initialization h = ThreadLocalRandom.getProbe(); wasUncontended = true; } //标识是否有冲突,如果最后一个桶不是null,那么为true boolean collide = false; // True if last slot nonempty for (;;) { CounterCell[] as; CounterCell a; int n; long v; //数组不为空,优先对数组中CouterCell的value累加 if ((as = counterCells) != null && (n = as.length) > 0) { //线程对应的桶位为null if ((a = as[(n - 1) & h]) == null) { if (cellsBusy == 0) { // Try to attach new Cell //创建CounterCell对象 CounterCell r = new CounterCell(x); // Optimistic create //利用CAS修改cellBusy状态为1,成功则将刚才创建的CounterCell对象放入数组中 if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { boolean created = false; try { // Recheck under lock CounterCell[] rs; int m, j; //桶位为空, 将CounterCell对象放入数组 if ((rs = counterCells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { rs[j] = r; //表示放入成功 created = true; } } finally { cellsBusy = 0; } if (created) //成功退出循环 break; //桶位已经被别的线程放置了已给CounterCell对象,继续循环 continue; // Slot is now non-empty } } collide = false; } //桶位不为空,重新计算线程hash值,然后继续循环 else if (!wasUncontended) // CAS already known to fail wasUncontended = true; // Continue after rehash //重新计算了hash值后,对应的桶位依然不为空,对value累加 //成功则结束循环 //失败则继续下面判断 else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x)) break; //数组被别的线程改变了,或者数组长度超过了可用cpu大小,重新计算线程hash值,否则继续下一个判断 else if (counterCells != as || n >= NCPU) collide = false; // At max size or stale //当没有冲突,修改为有冲突,并重新计算线程hash,继续循环 else if (!collide) collide = true; //如果CounterCell的数组长度没有超过cpu核数,对数组进行两倍扩容 //并继续循环 else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { try { if (counterCells == as) {// Expand table unless stale CounterCell[] rs = new CounterCell[n << 1]; for (int i = 0; i < n; ++i) rs[i] = as[i]; counterCells = rs; } } finally { cellsBusy = 0; } collide = false; continue; // Retry with expanded table } h = ThreadLocalRandom.advanceProbe(h); } //CounterCell数组为空,并且没有线程在创建数组,修改标记,并创建数组 else if (cellsBusy == 0 && counterCells == as && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { boolean init = false; try { // Initialize table if (counterCells == as) { CounterCell[] rs = new CounterCell[2]; rs[h & 1] = new CounterCell(x); counterCells = rs; init = true; } } finally { cellsBusy = 0; } if (init) break; } //数组为空,并且有别的线程在创建数组,那么尝试对baseCount做累加,成功就退出循环,失败就继续循环 else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x)) break; // Fall back on using base } }

图解

fullAddCount方法中,当as数组不为空的逻辑图解

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsdsy.html