===
- package leetcode0606.JUC.ConcurrentHashMap;
-
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ThreadLocalRandom;
-
- public class _addCount {
-
- addCount(1L, binCount);
-
- private final void addCount(long x, int check) {
- /*
- check
- >=1 当前桶位 链表的长度 或者 发生冲突的位置
- =0 当前桶位的 头节点为null
- 2 当前桶位为红黑树
- */
-
- /*
- LongAdder
- */
- // s 当前map.table中的元素数量
- // b 表示 LongAdder 中的base值
- // as 表示cells数组
- ConcurrentHashMap.CounterCell[] as; long b, s;
- /*
- 条件一: as = counterCells) != null
- true 表示cells数组已经初始化,当前线程使用hash去寻址找到合适的cell,去累加数据
- false 走一下个跳进
- =====================================================================================================
- 条件二: !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)
- true 说明cells数组未初始化,该线程只能去写basecount,结果失败了,即与其它线程发生了冲突,当前线程尝试去创建cells数组
- false 说明写basecount成功,不需要创建cells数组,直接退出。
- */
- if ((as = counterCells) != null ||
- !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
- /*
- a 当前哈希寻址命中的cell
- v 写cell的期望值
- m 当前cells数组的长度
- */
- ConcurrentHashMap.CounterCell a; long v; int m;
- // true 未竞争
- boolean uncontended = true;
- /*
- 前置条件: true 表示cells数组已经初始化,当前线程使用hash去寻址找到合适的cell,去累加数据
- true 说明cells数组未初始化,该线程只能去写basecount,结果失败了,即与其它线程发生了冲突,当前线程尝试去创建cells数组
- ----------------------------------------------------------------------------------------------------
- 条件一:as == null || (m = as.length - 1) < 0
- true cells数组未初始化,且写base失败,进入 fullAddCount() 去扩容或者重试
- 条件二:a = as[ThreadLocalRandom.getProbe() & m]) == null
- true cells已经初始化了,且哈希寻址的cell为null,需要创建新的cell节点,放入cells数组中。
- 条件三:!(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
- true 当前cell单元格不为null,有值了,去进行CAS累加操作,若竞争失败,进入以下方法,进行多次尝试,若仍然失败,进行扩容。
- */
- if (as == null || (m = as.length - 1) < 0 ||
- (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
- !(uncontended =
- U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
- // 与 LongAdder 的核心方法一致
- fullAddCount(x, uncontended);
- return;
- }
- if (check <= 1)
- return;
- s = sumCount();
- }
-
- ============================================================================================
- ============================================================================================
-
- // 扩容
- // check >=0 表示是put()方法调用的addCount();
- // check <0 表示是remove()方法调用的addCount();
- if (check >= 0) {
- // tab 老数组
- // nt (nexttable) 扩容之后的数组
- // n table的长度
- // sc sizeCtl的临时变量
- ConcurrentHashMap.Node
[] tab, nt; int n, sc; - // sizeCtl < 0 大概率是-1
- // -1 表示table正在初始化(有其它线程在创建table数组),当前线程需要自旋等
- //======================table数组正在扩容,高16位表示 扩容的标识戳 低16位表示 (1+nThread) ,参与并发扩容的线程数量。======================
- // sizeCtl = 0
- // 表示创建table数组时,使用 DEFAULT_CAPACITY大小
- //
- // sizeCtl > 0
- // 如果table未初始化,表示初始化大小 ,如果调用有参构造器,其中有这么一句 this.sizeCtl = cap;
- // 如果调用无参的那么sizeCtl = 0,走默认长度 -----int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
- // =======================如果已经初始化,表示下次扩容时的 触发条件(阈值)=================================
-
- // s 当前map.table中的元素数量
- /*
- 条件一:s >= (long)(sc = sizeCtl)
- true 当前sizeCtl是一个负数,即正在扩容中
- 当前sizeCtl是一个整数,表示这次扩容的阈值,要扩容了
- false 未达到扩容阈值,选择不扩容
- 条件二,三: (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY 恒成立
- */
- while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
- (n = tab.length) < MAXIMUM_CAPACITY) {
- // 拿到扩容唯一标识戳
- // 假如从 16到32 ,那么 rs = 1000 0000 0001 1011
- int rs = resizeStamp(n);
- /*
- 当前table正在扩容,理论上当前线程要协助map扩容
- */
- if (sc < 0) {
- /*
- 条件一:(sc >>> RESIZE_STAMP_SHIFT) != rs 检查标尺戳使得否为对应的扩容
- 条件二: sc == (rs << 16) + 1
- true 标识扩容完毕了,当前线程不需要再参与了
- false 还在进行中,可以参与
- 条件三: sc == (rs << 16)+ MAX_RESIZERS .....
- 条件四: (nt = nextTable) == null
- true 扩容结束
- */
- if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
- sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
- transferIndex <= 0)
- break;
- // sc 低16位是 (1+nThread)
- // true 参与并发扩容的线程+1
- // false CAS失败,参与竞争的线程比较多,然后自旋
- if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
- // 协助扩容线程
- transfer(tab, nt);
- }
- /*
- 当前table
- // 高16位表示 扩容的标识戳 低16位表示 (1+nThread)
- // 1000 0000 0001 1011 , 0000 0000 0000 0010
- // +2说明当前线程是第一个触发扩容的线程,干的活多一些,修改完sc后你会发现它变成了一个负数,此操作也是在多线程的场景下进行的,当CAS操作失败后
- // 进入自旋,走 sc<0 的分支
- */
- else if (U.compareAndSwapInt(this, SIZECTL, sc,
- (rs << RESIZE_STAMP_SHIFT) + 2))
- // 触发扩容线程
- transfer(tab, null);
- s = sumCount();
- }
- }
- }
- }