ConcurrentHashMap 源码解析(四):put()方法超硬核源码解析

ConcurrentHashMap中的put()方法是其中一个比较复杂的方法,其中涉及到了很多的知识点。比如,迁移时的多线程协助、桶位树化、多线程累加等等。下面我将详细解析put()方法和所涉及到的所有子方法的源码。

put()方法中其实是调用了putVal()来实现的。

/**
  * Maps the specified key to the specified value in this table.
  * Neither the key nor the value can be null.
  *
  * <p>The value can be retrieved by calling the {@code get} method
  * with a key that is equal to the original key.
  *
  * @param key key with which the specified value is to be associated
  * @param value value to be associated with the specified key
  * @return the previous value associated with {@code key}, or
  *         {@code null} if there was no mapping for {@code key}
  * @throws NullPointerException if the specified key or value is null
  */
 public V put(K key, V value) {
     return putVal(key, value, false);
 }

putVal()方法会返回插入数据时发生冲突的值。整个方法大体步骤是:在多线程执行此方法时,如果遇到当前table正在扩容,则需要先协助扩容且扩容成功后再执行插入操作。插入成功后还需要判断当前桶位是否需要进行树化。最后,统计当前table元素个数,判断是否需要扩容。

/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
    //控制key和value不能为空 hashmap允许是空
    if (key == null || value == null) throw new NullPointerException();
    //rehash
    int hash = spread(key.hashCode());
    //binCount 表示当前k-v封装成node后,插入到指定桶位后在链表中所属的下标
    //0 表示当前要插入的桶位是null,node直接插入成为头结点
    //>=1 表示当前桶位上node直接插入到链表的位置(位置可能不是表尾,如果插入时有冲突)
    //2 表示当前要插入的桶位可能树化成红黑树
    int binCount = 0;
    //自旋
    //tab 引用当前的table对象
    Node<K,V>[] tab = table;
    for (;;) {
        //f: 表示桶位的头结点
        //n: 表示散列表数组的长度
        //i: 表示key通过寻址计算后,得到的桶位下标
        //fh: 表示桶位头结点的hash值
        Node<K,V> f; int n, i, fh;

        //CASE1: true -> 表示当前table还未初始化
        if (tab == null || (n = tab.length) == 0)
            //初始化table
            tab = initTable();
        //前置条件: table已经初始化
        //tabAt: 根据指定的table和地址,到主存中获取对应的值,如果有值,返回的是链表头结点
        //CASE2: true -> 当前桶位为空,可以直接插入
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            //true -> 将要插入的元素封装成node节点,然后插入到table下标为i并且值为null的桶位上
            //false -> 进行下一次自旋
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                //插入成功,退出自旋
                break;                   // no lock when adding to empty bin
        }
        //前置条件: 1、table已经初始化 2、当前桶位已经有节点了
        //CASE3: true -> 表示当前头结点为 FWD 节点,说明目前table正在扩容中
        else if ((fh = f.hash) == MOVED)
            //表示当前线程要帮助其它线程进行迁移
            tab = helpTransfer(tab, f);
        //前置条件: 1、table已经初始化 2、当前桶位已经有节点了 3、当前table并没有在扩容
        //CASE4: true -> 表示当前桶位可能是 链表 也可能是 treebin 红黑树代理节点 
        else {
            //当key已存在,将旧值赋值给oldVal,并返回
            V oldVal = null;
            //获取对应桶位头元素f 的锁
            synchronized (f) {
                //这里也用了双重检验 避免有其它线程把这个桶位上的头结点改了,导致这个锁加错元素
                if (tabAt(tab, i) == f) {
                    //true -> 说明当前桶位是普通链表节点
                    if (fh >= 0) {
                        //CASE1: 当前插入key与链表中所有的key都不一致时,当前的插入操作是追加到链表的末尾,binCount表示插入成功后链表长度
                        //CASE2: 当前插入key与链表中某个节点的key一致时,当前的插入操作是替换,binCount表示冲突的位置 binCount - 1
                        binCount = 1;
                        //循环遍历链表
                        //e: 表示当前循环的节点
                        for (Node<K,V> e = f;; ++binCount) {
                            //ek: 表示当前循环节点的key
                            K ek;
                            //条件1: true -> 链表中某个节点的hash与传入的hash匹配,进行下一步判断
                            //条件2: true -> 该链表节点的key与传入节点的key也匹配,进入方法体内
                            if (e.hash == hash &&
                                ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
                                //把匹配到的链表节点的value赋值给 oldVal
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    //value值替换操作
                                    e.val = value;
                                //退出自旋
                                break;
                            }
                            //当前链表节点与传入的节点不匹配,走下面逻辑
                            //pred: 表示当前链表节点前一个节点
                            Node<K,V> pred = e;
                            //将e节点指针指向next元素,并判断是否为空
                            // true -> 表示当前链表节点已经是最后一个,下一步就是把传入的元素插入到末尾
                            // false -> 表示当前链表节点还不是最后一个,继续下一个循环
                            if ((e = e.next) == null) {
                                //将传入的节点插入到表尾
                                //此时的pred是指向当前链表节点
                                pred.next = new Node<K,V>(hash, key, value, null);
                                //退出自旋
                                break;
                            }
                        }
                    }
                    //前置条件: 该桶位不是链表
                    //true -> 说明当前桶位是红黑树代理节点
                    else if (f instanceof TreeBin) {
                        //p: 表示进入红黑树查找时,匹配到一个已存在的节点,会将这个节点返回
                        Node<K,V> p;
                        //binCount设置为2是因为 binCount <= 1 都已经有对应的含义了
                        binCount = 2;
                        //true -> 表示在插入到红黑树的过程中,匹配到一个已经存在的节点,将这个节点的引用返回给 p ,进行下一步操作
                        //false -> 表示直接插入成功,没有遇到冲突
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
                            //将旧值赋值给 oldVal
                            oldVal = p.val;
                            //true -> 不允许替换
                            //false -> 允许替换
                            if (!onlyIfAbsent)
                                //用新值替换旧值
                                p.val = value;
                        }
                    }
                }
            }
            //下面的代码块的作用是 1、判断当前桶位是否需要树化 2、如果插入时有冲突,直接返回冲突值
            //true -> 表示当前桶位不为null,可能是红黑树,可能是链表
            if (binCount != 0) {
                //true -> 如果binCount >= 8 表示当前桶位一定是链表,而且达到树化的条件(注意,这里并不一定会树化,还需要看整个table的元素个数)
                if (binCount >= TREEIFY_THRESHOLD)
                    //树化操作
                    treeifyBin(tab, i);
                //true -> 表示插入时发生了冲突,并将冲突节点的值返回
                if (oldVal != null)
                    //返回冲突节点的值
                    return oldVal;
                break;
            }
        }
    }
    //作用: 1、统计当前table一共有多少元素
    //     2、判断是否达到扩容标准,触发扩容
    addCount(1L, binCount);
    return null;
}

initTable()方法是putVal()方法中初始化table时调用的。

/**
  * Initializes table, using the size recorded in sizeCtl.
  */
 private final Node<K,V>[] initTable() {
     //tab: 当前table的引用
     //sc: 临时存放sizeCtl的局部变量
     Node<K,V>[] tab; int sc;
     //带条件的自旋 如果table没有初始化就一直自旋
     while ((tab = table) == null || tab.length == 0) {
         //sc大概率是-1 表示有线程正在进行创建table的过程,当前线程没有竞争到锁
         if ((sc = sizeCtl) < 0)
             //释放cpu资源
             Thread.yield(); // lost initialization race; just spin
         //true -> 表示获取锁成功,开始执行初始化逻辑
         else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
             try {
                 //双重验证 防止有其它线程已经初始化完毕了,而当前线程再次初始化,所以需要双重判断一次
                 //true -> 说明还未有线程初始化当前map的table
                 if ((tab = table) == null || tab.length == 0) {
                     //sc > 0 true -> 初始化数组的大小 = 指定的大小sc
                     //       false -> 初始化数组的大小 = 默认的大小 16
                     int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                     @SuppressWarnings("unchecked")
                     //创建一个以n为长度的node数组
                     Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                     //将创建出来的数组 赋值给 map.table
                     table = tab = nt;
                     //n >>> 2 -> (1/4) * n
                     //n - (n >>> 2) -> n - (1/4) * n = (3/4) * n = 0.75 * n 表示扩容阈值
                     sc = n - (n >>> 2);
                 }
             } finally {
                 //前置: 此时的 sizeCtl 在获取锁成功后是 = -1
                 //CASE1: 如果当前线程是初始化map.table的线程,sizeCtl = 下次扩容的阈值
                 //CASE2: 如果当前线程获取锁后发现table已经被其它线程初始化了,sizeCtl = 原来的值
                 sizeCtl = sc;
             }
             break;
         }
     }
     return tab;
 }

helpTransfer()方法是线程调用putVal()方法时,发现table正在扩容,此时,当前线程需要协助其它线程进行扩容操作。

/**
  * Helps transfer if a resize is in progress.
  */
 final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
     //nextTab: 迁移时的新table的引用
     //sc: sizeCtl临时变量
     Node<K,V>[] nextTab; int sc;
     if (tab != null && (f instanceof ForwardingNode) && (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
         //获取扩容标识戳
         int rs = resizeStamp(tab.length);
         //条件1: true -> 表示当前扩容还未完成
         //      false -> 表示当前扩容已完成 扩容后nextTable会被设置为null
         //条件2: true -> 表示当前扩容还未完成
         //      false -> 表示当前扩容已完成 扩容后table会被赋值为nextTable的值
         //条件3: true -> 表示当前扩容还未完成
         //      false -> 表示当前扩容已完成 扩容后sizeCtl会被赋值为下次扩容的阈值
         while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) {
             //条件1: true -> 当前线程获取到的扩容唯一标识戳非本次扩容批次,执行方法体
             //      false -> 当前线程获取到的扩容唯一标识戳是本次扩容批次,进入下一个判断
             //条件2: jdk1.8有bug 这里应该是 sc == (rs << RESIZE_STAMP_SHIFT) + 1
             //      true -> 表示所有线程都执行完毕了 线程数量是 = 1+n,执行方法体
             //      false -> 表示还在扩容中,进入下一个判断
             //条件3: jdk1.8有bug 这里应该是 sc == (rs << RESIZE_STAMP_SHIFT) + MAX_RESIZERS
             //      true -> 表示当前参与扩容的线程数量达到最大限度了,不能再参与了,执行方法体
             //      false -> 表示当前参与扩容的线程数量还未达到最大限度,当前线程可以参与,进入下一个判断
             //条件4: true -> 表示扩容已完毕,执行方法体
             //      false -> 表示扩容还在进行,进入下一个判断
             //条件5: true -> 表示全局范围内的任务已经分配完了,执行方法体
             //      false -> 表示还有任务可分配,结束判断
             if ((sc >>> RESIZE_STAMP_SHIFT) != rs ||
                 sc == rs + 1 ||
                 sc == rs + MAX_RESIZERS ||
                 transferIndex <= 0)
                 break;
             //true -> 表示当前线程有任务可分配,将进入协助扩容
             if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                 //扩容方法
                 transfer(tab, nextTab);
                 break;
             }
         }
         //返回最新的新table引用(可能还在扩容中)
         return nextTab;
     }
     return table;
 }

/**
  * Returns the stamp bits for resizing a table of size n.
  * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
  * 返回用于扩容大小为n的表的标识戳。
  * 16 -> 32
  * n: 16 -> 0000 0000 0000 0000 0000 0000 0001 0000
  * Integer.numberOfLeadingZeros(16): 27
  * 1: 0000 0000 0000 0000 0000 0000 0000 0001
  * 1 << (RESIZE_STAMP_BITS - 1) => 1 << 15: 0000 0000 0000 0000 1000 0000 0000 0000 -> 32768
  * --------------------------------------------------------------------------------
  * 0000 0000 0000 0000 0000 0000 0001 1011 -> 27
  * |
  * 0000 0000 0000 0000 1000 0000 0000 0000 -> 32768
  * 0000 0000 0000 0000 1000 0000 0001 1011 -> 计算出来的就是返回的标识戳
  */
 static final int resizeStamp(int n) {
     return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
 }

/**
  * Moves and/or copies the nodes in each bin to new table. See
  * above for explanation.
  */
 private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
     //n: 表示扩容前table的长度
     //stride: 表示每个线程分配到任务数量
     int n = tab.length, stride;
     //计算每个线程分配的任务数量,最小值是16
     if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
         stride = MIN_TRANSFER_STRIDE; // subdivide range

     //true -> 表示当前线程是第一个触发扩容的线程,需要做一些扩容准备
     //false -> 表示当前线程是协助扩容的线程
     if (nextTab == null) {            // initiating
         try {
             @SuppressWarnings("unchecked")
             //创建一个大一倍的数组 2n
             Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
             //将nt赋值给临时变量nextTab
             nextTab = nt;
         } catch (Throwable ex) {      // try to cope with OOME
             sizeCtl = Integer.MAX_VALUE;
             return;
         }
         //将临时变量nextTab赋值给全局变量nextTable,为了让协助线程能获取到
         nextTable = nextTab;
         //记录迁移数据整体进度的标记,index计数是从1开始的
         transferIndex = n;
     }

     //表示新表长度
     int nextn = nextTab.length;
     //fwd 节点,当某个桶位数据处理完毕后,将此桶位设置为fwd节点,其它写线程 或 读线程看到后会有不同逻辑
     ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
     //推进标记
     boolean advance = true;
     //完成标记
     boolean finishing = false; // to ensure sweep before committing nextTab
     //i: 表示分配给当前线程任务,执行到的桶位(从后往前执行)
     //bound: 表示分配给当前线程任务的下界限制
     int i = 0, bound = 0;

     //自旋
     for (;;) {
         //f: 桶位的头结点
         //fh: 桶位头结点的hash
         Node<K,V> f; int fh;
         //1、给当前线程分配任务区间
         //2、维护当前线程任务进度(i 表示当前处理的桶位)
         //3、维护table 全局范围内的进度(transferIndex)
         while (advance) {
             //nextIndex: 新分配任务的开始下标(以1开始计数的)
             //nextBound: 新分配任务的结束下标
             int nextIndex, nextBound;
             //条件1: true -> 表示当前线程还未完成所有已分派的任务,--i 表示让线程处理下一个分配的桶位
             //      false -> 表示当前线程已完成所有已分派的任务 或者 还未分派任务
             //条件2: true -> 表示迁移任务已完成,准备做最后的检查操作 注意: 此时条件1的 --i 是控制检查的桶位
             if (--i >= bound || finishing)
                 advance = false;
             //前置条件: 当前线程已完成所有已分派的任务 或者 还未分派任务
             //条件2: true -> 表示当前任务池中已经没有剩余的任务可分配了
             //      false -> 表示当前任务池中还有可分配的任务
             else if ((nextIndex = transferIndex) <= 0) {
                 i = -1;
                 advance = false;
             }
             //前置条件: 1.当前线程还未分派任务
             //         2.当前任务池中还有可分配的任务
             //分配任务 nextBound = 0 表示把剩下的任务都分派给当前线程
             //true -> 为当前线程分派任务成功
             //false -> 为当前线程分派任务失败,有其它线程抢先分得了任务
             else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex,
                       nextBound = (nextIndex > stride ? nextIndex - stride : 0))) {
                 bound = nextBound;
                 //因为transferIndex是以1开始计数的,所以这里需要-1
                 i = nextIndex - 1;
                 advance = false;
             }
         }
         //条件1: true -> 表示当前线程未分配到任务
         //      false -> 进入下一个判断
         //条件2、3恒不成立
         if (i < 0 || i >= n || i + n >= nextn) {
             //sizeCtl临时变量
             int sc;
             //所有桶位都检查完后执行
             if (finishing) {
                 //将concurrentHashMap.nextTable置空 GC
                 nextTable = null;
                 //将新table赋值给全局的table
                 table = nextTab;
                 //设置新的扩容阈值为0.75n  2n - n/2 = 3/2n 相当于 n - n/4 = 3/4n 这里用2n是因为扩容后的数组长度为2n
                 sizeCtl = (n << 1) - (n >>> 1);
                 //所有迁移任务完成,并检查完毕,正常退出
                 return;
             }
             //true -> 表示没有任务分配给当前线程,准备正常退出,sc - 1
             if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                 //true -> 当前线程不是最后一个退出的线程
                 if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                     //正常退出
                     return;
                 //将 finishing、advance 都设置为true,当前线程将要做结束迁移任务的检查操作
                 finishing = advance = true;
                 //把i设置成老table的长度,为了从后往前检查是否都迁移完毕
                 i = n; // recheck before commit
             }
         }
         //tab: 旧table引用
         //前置条件: 当前线程任务尚未处理完,正在进行中。
         //true -> 说明当前桶位上未存放数据,只需要将此处设置为fwd节点即可,执行方法体。
         //false -> 说明当前桶位上有数据,进入下一个判断。
         else if ((f = tabAt(tab, i)) == null)
             //使用cas把当前节点设置为fed节点
             advance = casTabAt(tab, i, null, fwd);
         //前置条件: 1、当前线程任务尚未处理完,正在进行中。 2、当前桶位上有数据
         //true -> 表示当前桶位上的节点已经迁移成功,再次自旋,进入下一个桶位。
         //false -> 表示当前桶位上的节点需要迁移,进入下一个判断。
         else if ((fh = f.hash) == MOVED)
             advance = true; // already processed
         //前置条件: 1、当前线程任务尚未处理完,正在进行中。 2、当前桶位上有数据 3、当前桶位上的节点需要迁移
         //执行迁移操作
         else {
             //给当前桶位上的头结点加锁
             synchronized (f) {
                 //双重判断,防止有其它线程改了头结点,导致上错锁
                 if (tabAt(tab, i) == f) {
                     //ln: 表示低位链表的引用
                     //hn: 表示高位链表的引用
                     Node<K,V> ln, hn;
                     //true -> 表示当前桶位是链表桶位
                     if (fh >= 0) {
                         //可以获得当前链表 末尾连续高位不变的node
                         //n恒定为2的次方数,因此,fh & n 只会得到高位是0或者1的二进制数
                         //获取头节点的迁移标识(0 或 n)
                         int runBit = fh & n;
                         //将头结点赋值给lastRun
                         Node<K,V> lastRun = f;
                         //p: 当前遍历的节点,从头结点的下一个节点开始
                         for (Node<K,V> p = f.next; p != null; p = p.next) {
                             //获取当前节点的迁移标识(0 或 n)
                             int b = p.hash & n;
                             //true -> 当前节点的迁移标识与上一个节点的不一致
                             //false -> 当前节点的迁移标识与上一个节点的一致
                             if (b != runBit) {
                                 //将runBit设置为当前节点的迁移标识
                                 runBit = b;
                                 //将lastRun设置为当前节点
                                 lastRun = p;
                             }
                         }
                         //末尾连续高位是0
                         if (runBit == 0) {
                             //此时lastRun引用的是连续迁移标识是0的低位链表,并将ln指向它
                             ln = lastRun;
                             //清空高位链表
                             hn = null;
                         }
                         //末尾连续高位是n
                         else {
                             //此时lastRun引用的是连续迁移标识是n的高位链表,并将hn指向它
                             hn = lastRun;
                             //清空低位链表
                             ln = null;
                         }
                         //遍历链表到lastRun节点的位置为止
                         for (Node<K,V> p = f; p != lastRun; p = p.next) {
                             int ph = p.hash; K pk = p.key; V pv = p.val;
                             //如果迁移标识是0
                             if ((ph & n) == 0)
                                 //创建一个新的低位链表,并将当前节点放入,且next指针指向旧的低位链表
                                 ln = new Node<K,V>(ph, pk, pv, ln);
                             else
                                 //创建一个新的高位链表,并将当前节点放入,且next指针指向旧的高位链表
                                 hn = new Node<K,V>(ph, pk, pv, hn);
                         }
                         //低位链表设置到新table 原来的桶位上
                         setTabAt(nextTab, i, ln);
                         //高位链表设置到新table 原来的桶位 + 扩容大小的桶位上
                         setTabAt(nextTab, i + n, hn);
                         //将迁移好的桶位设置为fwd,表示已迁移
                         setTabAt(tab, i, fwd);
                         //表示任务继续执行
                         advance = true;
                     }
                     //true -> 表示当前桶位是 红黑树代理节点 treebin
                     else if (f instanceof TreeBin) {
                         //将头结点转换为treebin
                         TreeBin<K,V> t = (TreeBin<K,V>)f;
                         //低位双向链表 lo 指向低位链表头结点 loTail 指向低位链表尾结点
                         TreeNode<K,V> lo = null, loTail = null;
                         //高位双向链表 hi 指向高位链表头结点 hiTail 指向高位链表尾结点
                         TreeNode<K,V> hi = null, hiTail = null;
                         //lc 表示低位链表元素数量
                         //hc 表示高位链表元素数量
                         int lc = 0, hc = 0;
                         //遍历treebin中的双向链表 从头结点至尾结点
                         for (Node<K,V> e = t.first; e != null; e = e.next) {
                             //当前循环节点的hash值
                             int h = e.hash;
                             //用当前循环节点创建出来的新的treeNode节点
                             TreeNode<K,V> p = new TreeNode<K,V>(h, e.key, e.val, null, null);
                             //当前循环节点属于低位双向链表
                             if ((h & n) == 0) {
                                 //采用尾插法
                                 //说明当前低位链表还没有值
                                 if ((p.prev = loTail) == null)
                                     //将低位链表头指针指向当前循环节点
                                     lo = p;
                                 //说明当前低位链表有值
                                 else
                                     //将尾指针指向的元素的next指针指向当前循环节点
                                     loTail.next = p;
                                 //将低位链表尾指针指向当前循环节点
                                 loTail = p;
                                 //累加低位链表元素数量
                                 ++lc;
                             }
                             //当前循环节点属于高位双向链表
                             else {
                                 //采用尾插法
                                 //说明当前高位链表还没有值
                                 if ((p.prev = hiTail) == null)
                                     //将高位链表头指针指向当前循环节点
                                     hi = p;
                                 //说明当前高位链表有值
                                 else
                                     //将尾指针指向的元素的next指针指向当前循环节点
                                     hiTail.next = p;
                                 //将高位链表尾指针指向当前循环节点
                                 hiTail = p;
                                 //累加高位链表元素数量
                                 ++hc;
                             }
                         }
                         //(lc <= 6) 判断低位链表中元素的个数 true -> 不需要树化 false -> 需要树化
                         //(hc != 0) 判断高位链表中是否有元素 true -> 将低位链表变成红黑树 false -> 用原来低位链表的treebin
                         ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : (hc != 0) ? new TreeBin<K,V>(lo) : t;
                         //(hc <= 6) 判断高位链表中元素的个数 true -> 不需要树化 false -> 需要树化
                         //(lc != 0) 判断低位链表中是否有元素 true -> 将高位链表变成红黑树 false -> 用原来高位链表的treebin
                         hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : (lc != 0) ? new TreeBin<K,V>(hi) : t;
                         //将低位链表迁移到新table 与旧表一致 的桶位上
                         setTabAt(nextTab, i, ln);
                         //将高位链表迁移到新table 旧表桶位 + 扩容大小 的桶位上
                         setTabAt(nextTab, i + n, hn);
                         //将原表对应的迁移好的桶位设置为fwd桶位
                         setTabAt(tab, i, fwd);
                         //继续执行,进入下个桶位
                         advance = true;
                     }
                 }
             }
         }
     }
 }

putTreeVal()方法是将元素插入到红黑树中。

/**
  * Finds or adds a node.
  * @return null if added
  */
 final TreeNode<K,V> putTreeVal(int h, K k, V v) {
     //kc: key的class
     Class<?> kc = null;
     //searched: 是否有尝试从红黑树中匹配待插入元素的标记
     boolean searched = false;
     //p: 游标指针 从root节点开始往下查找
     TreeNode<K,V> p = root;
     for (;;) {
         //dir: 表示待插入节点在父节点的左边 或 右边
         //ph: 当前游标节点的hash值
         //pk: 当前游标节点的key
         int dir, ph; K pk;
         //true -> 表示当前的树和链表都是空的
         if (p == null) {
             //将待插入节点封装为treeNode 并同时让 链表的头指针 和 红黑树的头结点指针 指向它
             first = root = new TreeNode<K,V>(h, k, v, null, null);
             break;
         }
         //true -> 当前游标节点的hash值比待插入节点的hash值大
         else if ((ph = p.hash) > h)
             //表示待插入节点位于游标节点的左边
             dir = -1;
         //true -> 当前游标节点的hash值比待插入节点的hash值小
         else if (ph < h)
             //表示待插入节点位于游标节点的右边
             dir = 1;
         //前置条件: 当前游标节点的hash值与待插入节点的hash值相等
         //true -> 当前游标节点的key也与待插入节点的key相等
         else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
             //待插入节点已存在,直接返回匹配到的节点
             return p;
         //前置条件: 1、当前游标节点的hash值与待插入节点的hash值相等
         //         2、且当前游标节点的key也与待插入节点的key不相等
         //条件1: true -> 通过comparableClassFor(k)也获取不到key的class类型,进入方法体
         //      false -> 通过comparableClassFor(k)获取到了key的class类型,进入下一个判断
         //条件2: true -> 通过compareComparables(kc, k, pk)计算不出dir,进入方法体
         //      false -> 通过compareComparables(kc, k, pk)计算出dir,结束判断
         else if ((kc == null && (kc = comparableClassFor(k)) == null) ||
                  (dir = compareComparables(kc, k, pk)) == 0) {
             //是否有尝试从红黑树中匹配待插入元素
             if (!searched) {
                 TreeNode<K,V> q, ch;
                 //标记设置为true
                 searched = true;
                 //条件1: true -> 迭代当前游标节点的左子树的所有元素 查找到了与待插入节点相同的元素
                 //条件2: true -> 迭代当前游标节点的右子树的所有元素 查找到了与待插入节点相同的元素
                 if (((ch = p.left) != null && (q = ch.findTreeNode(h, k, kc)) != null) ||
                     ((ch = p.right) != null && (q = ch.findTreeNode(h, k, kc)) != null))
                     //返回匹配的元素
                     return q;
             }
             //如果还是找不到相同的元素,则计算dir 该方法一定会返回 1 或 -1
             dir = tieBreakOrder(k, pk);
         }
         //xp: 当前游标节点的父节点
         TreeNode<K,V> xp = p;
         //true -> 表示已经找到需要插入的位置 p指向的位置
         if ((p = (dir <= 0) ? p.left : p.right) == null) {
             //x: 待插入节点转换为TreeNode类型
             //f: 双向链表临时头结点
             TreeNode<K,V> x, f = first;
             //双向链表的维护 采用头插法 first指针指向待插入节点
             first = x = new TreeNode<K,V>(h, k, v, f, xp);

             if (f != null)
                 //将老头结点的prev指针指向新头结点  头插法
                 f.prev = x;

             if (dir <= 0)
                 //待插入节点位于父节点的左边
                 xp.left = x;
             else
                 //待插入节点位于父节点的右边
                 xp.right = x;

             //如果父节点是黑色的
             if (!xp.red)
                 //当前节点直接变为红色就可以了 没有破坏红黑数的平衡
                 x.red = true;
             //如果父节点是红色的
             else {
                 //会出现 红红相连 的情况
                 //给根节点上锁 准备调用平衡方法
                 lockRoot();
                 try {
                     //平衡红黑树,使其再次符合红黑树结构
                     root = balanceInsertion(root, x);
                 } finally {
                     //释放锁
                     unlockRoot();
                 }
             }
             break;
         }
     }
     assert checkInvariants(root);
     return null;
 }

/**
  * Returns the TreeNode (or null if not found) for the given key
  * starting at given root.
  */
 final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
     if (k != null) {
         //游标节点 初始值为调用该方法的节点
         TreeNode<K,V> p = this;
         do  {
             //ph: 当前游标节点的hash值
             //dir: 待插入节点位于当前游标节点的 左边 或 右边
             //pk: 当前游标节点的key
             //q: 匹配到的节点
             int ph, dir; K pk; TreeNode<K,V> q;
             //pl: 当前游标节点的左子节点
             //pr: 当前游标节点的右子节点
             TreeNode<K,V> pl = p.left, pr = p.right;
             //CASE1: 当前游标节点的hash值 > 待插入节点的hash值
             if ((ph = p.hash) > h)
                 p = pl;
             //CASE2: 当前游标节点的hash值 < 待插入节点的hash值
             else if (ph < h)
                 p = pr;
             //前置条件: 当前游标节点的hash值 == 待插入节点的hash值
             //CASE3: 当前游标节点的key == 待插入的节点的key
             else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                 return p;
             //前置条件: 1、当前游标节点的hash值 == 待插入节点的hash值
             //        2、当前游标节点的key != 待插入的节点的key
             //CASE4: 当前游标节点的左子节点是null
             else if (pl == null)
                 p = pr;
             //前置条件: 1、当前游标节点的hash值 == 待插入节点的hash值
             //        2、当前游标节点的key != 待插入的节点的key
             //CASE4: 当前游标节点的右子节点是null
             else if (pr == null)
                 p = pl;
             //前置条件: 1、当前游标节点的hash值 == 待插入节点的hash值
             //        2、当前游标节点的key != 待插入的节点的key
             //        3、当前游标节点的左、右节点 != null
             //CASE5: 获取待插入节点的key的class类型 并计算出dir
             else if ((kc != null ||
                       (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0)
                 p = (dir < 0) ? pl : pr;
             //前置条件: 1、当前游标节点的hash值 == 待插入节点的hash值
             //        2、当前游标节点的key != 待插入的节点的key
             //        3、当前游标节点的左、右节点 != null
             //        4、无法通过待插入节点key的class类型计算dir
             //CASE6: 遍历当前游标节点的整个右子树
             else if ((q = pr.findTreeNode(h, k, kc)) != null)
                 return q;
             //前置条件: 1、当前游标节点的hash值 == 待插入节点的hash值
             //        2、当前游标节点的key != 待插入的节点的key
             //        3、当前游标节点的左、右节点 != null
             //        4、无法通过待插入节点key的class类型计算dir
             //        5、遍历当前游标节点的整个右子树还是未找到
             //CASE7: 从左子节点开始继续找
             else
                 p = pl;

         } while (p != null);
     }
     return null;
 }
}

treeifyBin()方法是树化操作。

/**
  * Replaces all linked nodes in bin at given index unless table is
  * too small, in which case resizes instead.
  */
 private final void treeifyBin(Node<K,V>[] tab, int index) {
     Node<K,V> b; int n, sc;
     if (tab != null) {
         //true -> 当前table的长度未达到64,不进行树化操作,执行扩容操作
         if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
             //将table长度扩大一倍
             tryPresize(n << 1);
         //前置条件: 当前table的长度已达到64
         //true -> 说明当前桶位有数据,且是普通的node节点
         else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
             //给链表的头结点上锁
             synchronized (b) {
                 //双重判断,防止有其它线程修改了此头结点,导致锁错对象
                 if (tabAt(tab, index) == b) {
                     //hd: 头指针
                     //tl: 尾指针
                     TreeNode<K,V> hd = null, tl = null;
                     //遍历链表 把单向链表转换为双向链表
                     for (Node<K,V> e = b; e != null; e = e.next) {
                         //将当前循环节点转换为双向链表
                         TreeNode<K,V> p = new TreeNode<K,V>(e.hash, e.key, e.val, null, null);
                         //当前双向链表为空
                         if ((p.prev = tl) == null)
                             //将头指针指向当前循环节点
                             hd = p;
                         //当前双向链表有值
                         else
                             //将尾指针指向的节点的next指针指向当前循环节点
                             tl.next = p;
                         //把尾指针指向当前循环节点
                         tl = p;
                     }
                     //new TreeBin<K,V>(hd)) 将双向链表转换为红黑树
                     setTabAt(tab, index, new TreeBin<K,V>(hd));
                 }
             }
         }
     }
 }

/**
  * Tries to presize table to accommodate the given number of elements.
  *
  * @param size number of elements (doesn't need to be perfectly accurate)
  */
 private final void tryPresize(int size) {
     //(size >= (MAXIMUM_CAPACITY >>> 1)) 判断需要创建的ConcurrentHashMap的大小是否超过最大值的一半
     //ture -> c = MAXIMUM_CAPACITY 最大值
     //false -> c = tableSizeFor(size + (size >>> 1) + 1) 能保证在插入指定数量的元素前不会触发扩容 牺牲内存,保证性能
     int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1);
     //sc: sizeCtl临时变量 默认是16
     int sc;
     //自旋 当 sc<0 时退出
     while ((sc = sizeCtl) >= 0) {
         //tab: table引用
         //n: 数组长度
         Node<K,V>[] tab = table; int n;
         //CASE1: 当前table还未初始化
         if (tab == null || (n = tab.length) == 0) {
             //获取sc 和 c 中较大的那位
             n = (sc > c) ? sc : c;
             //sizeCtl设置为 -1 表示加锁
             if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                 try {
                     //双重验证
                     if (table == tab) {
                         @SuppressWarnings("unchecked")
                         //创建一个长度为n的链表
                         Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                         //将新链表赋值给table
                         table = nt;
                         //设置下次扩容阈值 0.75n
                         sc = n - (n >>> 2);
                     }
                 } finally {
                     //sizeCtl = 下次扩容阈值
                     sizeCtl = sc;
                 }
             }
         }
         //前置条件: 当前table已经初始化
         //CASE2: 扩容大小 <= 扩容阈值 或 当前table长度已经是最大值了
         else if (c <= sc || n >= MAXIMUM_CAPACITY)
             break;
         //前置条件: 1、当前table已经初始化
         //        2、当前table准备扩容
         //CASE3: 当前table还未发生变化
         else if (tab == table) {
             int rs = resizeStamp(n);
             //true -> 表示table扩容中
             if (sc < 0) {
                 Node<K,V>[] nt;
                 //条件1: true -> 当前线程获取到的扩容唯一标识戳非本次扩容批次,执行方法体
                 //      false -> 当前线程获取到的扩容唯一标识戳是本次扩容批次,进入下一个判断
                 //条件2: jdk1.8有bug 这里应该是 sc == (rs << RESIZE_STAMP_SHIFT) + 1
                 //      true -> 表示所有线程都执行完毕了 线程数量是 = 1+n,执行方法体
                 //      false -> 表示还在扩容中,进入下一个判断
                 //条件3: jdk1.8有bug 这里应该是 sc == (rs << RESIZE_STAMP_SHIFT) + MAX_RESIZERS
                 //      true -> 表示当前参与扩容的线程数量达到最大限度了,不能再参与了,执行方法体
                 //      false -> 表示当前参与扩容的线程数量还未达到最大限度,当前线程可以参与,进入下一个判断
                 //条件4: true -> 表示扩容已完毕,执行方法体
                 //      false -> 表示扩容还在进行,进入下一个判断
                 //条件5: true -> 表示全局范围内的任务已经分配完了,执行方法体
                 //      false -> 表示还有任务可分配,结束判断
                 if ((sc >>> RESIZE_STAMP_SHIFT) != rs ||
                     sc == rs + 1 ||
                     sc == rs + MAX_RESIZERS ||
                     (nt = nextTable) == null ||
                     transferIndex <= 0)
                     break;
                 //当前线程协助扩容
                 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                     transfer(tab, nt);
             }
             //true -> 当前线程是第一个执行扩容的线程
             else if (U.compareAndSwapInt(this, SIZECTL, sc,(rs << RESIZE_STAMP_SHIFT) + 2))
                 transfer(tab, null);
         }
     }
 }

addCount()方法有2个作用,1、统计当前table一共有多少元素。 2、判断是否达到扩容标准,触发扩容。

/**
  * Adds to count, and if table is too small and not already
  * resizing, initiates transfer. If already resizing, helps
  * perform transfer if work is available.  Rechecks occupancy
  * after a transfer to see if another resize is already needed
  * because resizings are lagging additions.
  *
  * @param x the count to add
  * @param check if <0, don't check resize, if <= 1 only check if uncontended
  */
 private final void addCount(long x, int check) {
     //x: 需要累加的值
     //check: 0: 表示当前table中对应桶位只有头节点 >=1: 表示当前table中对应桶位是链表  2: 表示当前table中对应桶位是树
     //as: cells数组引用
     //b: baseCount
     //s: 统计table大小
     CounterCell[] as; long b, s;
     //条件1: true -> 表示cells数组已经创建,进入方法体内。
     //       false -> 表示cells数组还未创建,进行下一个判断。
     //条件2: true -> 表示通过cas把值累加到baseCount失败,说明出现了竞争,进入方法体内。
     //       false -> 表示通过cas把值累加到baseCount成功,结束当前判断。
     if ((as = counterCells) != null ||
         !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
         //a: 表示当前线程寻址命中的cell
         //v: 匹配到的cells数组对应桶位上的cell节点的value
         //m: cells数组长度
         CounterCell a; long v; int m;
         //uncontended: true -> 未发生竞争 false -> 发生竞争
         boolean uncontended = true;
         //场景1: cells数组已经创建。
         //场景2: cells数组未创建,但是在累加到baseCount时发生了竞争。
         //条件1: true -> 表示cells数组还未创建(通过场景2进入的),进入方法体 重试将传入的值累加到baseCount。
         //      false -> 表示cells数组已经创建,进入下一个判断。
         //条件2: true -> 表示匹配到的桶位上还未初始化cell节点,进入方法体 初始化cell。
         //      false -> 表示匹配到的桶位上已经初始化cell节点,进入下一个判断。
         //条件3: true -> 表示当前线程通过cas把传入的值累加到对应cells数组桶位上的cell节点失败,说明发生了竞争,进入方法体 cells数组扩容 或者 重试将传入的值累加到baseCount。
         //      false -> 表示当前线程通过cas把传入的值累加到对应cells数组桶位上的cell节点成功,结束当前判断。
         if (as == null || (m = as.length - 1) < 0 ||
             (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
             !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
             //初始化cell 或者 cells数组扩容 或者 重试将传入的值累加到baseCount
             //注意:uncontended为 false 才会今入方法体
             fullAddCount(x, uncontended);
             //考虑到fullAddCount会占用线程很长一段处理时间,因此不需要继续参与下面的操作
             return;
         }
         //只有当 check > 1 时,表示对应桶位新增元素后是链表或者树,才需要检查是否需要扩容
         if (check <= 1)
             return;
         //获取baseCount和每个cell节点中的value总和(这里只是拿到期望值,并不是最终值,得到结果后还可能有其它线程继续累加)
         s = sumCount();
     }
     //下面是table扩容方法
     //表示一定是put调用的addCount
     if (check >= 0) {
         //tab: map.table引用
         //nt: 需要迁移的table
         //n: table数组长度
         //sc: sizeCtl的临时值
         Node<K,V>[] tab, nt; int n, sc;
         //s: table中元素的个数
         //sizeCtl: CASE1: <0 表示当前table数组正在进行扩容,高16位表示:扩容的标识戳 低16位表示:(1 + nthread) 当前参与扩容的线程数量
         //         CASE2: >0 表示下次触发时的扩容条件(阈值)
         //条件1: true -> CASE1: 当前sizeCtl为负数,表示当前table数组正在进行扩容
         //              CASE2: 当前sizeCtl为正数,表示当前table中元素个数已经达到扩容条件
         //      false -> 表示当前table中元素个数没有达到扩容条件 或者 扩容完毕了
         //条件2: 恒成立
         //条件3: true -> 当前table长度没有超过最大限制
         //      false -> 当前table长度超过最大限制
         while (s >= (long)(sc = sizeCtl) &&
                 (tab = table) != null &&
                (n = tab.length) < MAXIMUM_CAPACITY) {
             //扩容批次唯一标识戳
             //比如: 16 -> 32 标识戳 = 0b 1000 0000 0001 1011
             int rs = resizeStamp(n);
             //true -> 表示当前table正在扩容,进入方法体。
             //false -> 表示当前table需要扩容,但是还未有线程操作,进入下一个判断。
             if (sc < 0) {
                 //条件1: true -> 当前线程获取到的扩容唯一标识戳非本次扩容批次,执行方法体
                 //      false -> 当前线程获取到的扩容唯一标识戳是本次扩容批次,进入下一个判断
                 //条件2: jdk1.8有bug 这里应该是 sc == (rs << RESIZE_STAMP_SHIFT) + 1
                 //      true -> 表示所有线程都执行完毕了 线程数量是 = 1+n,执行方法体
                 //      false -> 表示还在扩容中,进入下一个判断
                 //条件3: jdk1.8有bug 这里应该是 sc == (rs << RESIZE_STAMP_SHIFT) + MAX_RESIZERS
                 //      true -> 表示当前参与扩容的线程数量达到最大限度了,不能再参与了,执行方法体
                 //      false -> 表示当前参与扩容的线程数量还未达到最大限度,当前线程可以参与,进入下一个判断
                 //条件4: true -> 表示扩容已完毕,执行方法体
                 //      false -> 表示扩容还在进行,进入下一个判断
                 //条件5: true -> 表示全局范围内的任务已经分配完了,执行方法体
                 //      false -> 表示还有任务可分配,结束判断
                 if ((sc >>> RESIZE_STAMP_SHIFT) != rs ||
                     sc == rs + 1 ||
                     sc == rs + MAX_RESIZERS ||
                     (nt = nextTable) == null ||
                     transferIndex <= 0)
                     //当前线程无需参与扩容,退出自旋
                     break;
                 //前置条件: table正在扩容中,当前线程可以参与扩容
                 //true -> 当前线程获取 参与扩容的资格成功,执行方法体。
                 //false -> 当前线程获取参与扩容的资格失败,进入下一次自旋
                 if (U.compareAndSwapInt(this, SIZECTL, sc,sc + 1))
                     //协助其它线程扩容table,持有nextTable引用
                     transfer(tab, nt);
             }
             //RESIZE_STAMP_SHIFT: 16
             //rs << RESIZE_STAMP_SHIFT: 1000 0000 0001 1011 0000 0000 0000 0000
             //+2: 1000 0000 0001 1011 0000 0000 0000 0010 -> 表示低位存储线程数量 n+1
             //前置条件: 表示当前table需要扩容
             //true -> 表示当前线程是参加扩容的第一个线程,进入方法体。
             //false -> CASE1: 有其它线程抢先成为参加扩容的第一个线程修改了sizectl,导致cas失败,结束判断。
             //         CASE2: 有其它线程在执行transfer()后修改了sizectl,导致cas失败,结束判断。
             else if (U.compareAndSwapInt(this, SIZECTL, sc,(rs << RESIZE_STAMP_SHIFT) + 2))
                 //第一个线程开始扩容table,不持有nextTable
                 transfer(tab, null);
             //重新获取table元素数量,准备开始下一次自旋
             s = sumCount();
         }
     }
 }

// See LongAdder version for explanation
private final void fullAddCount(long x, boolean wasUncontended) {
    //x: 要累加的值
    //wasUncontended: 是否没有发生竞争 传入恒为false
    //h: 当前线程的hash值
    int h;
    if ((h = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();      // force initialization
        h = ThreadLocalRandom.getProbe();
        //将wasUncontended强制设为 true 表示当前无竞争
        wasUncontended = true;
    }
    //表示扩容意向
    boolean collide = false;                // True if last slot nonempty
    //自旋
    for (;;) {
        //as: cells数组引用
        //a: cells数组桶位上的cell
        //n: cells数组长度
        //v: cell中的value
        CounterCell[] as; CounterCell a; int n; long v;
        //true -> cells数组不为null
        if ((as = counterCells) != null && (n = as.length) > 0) {
            //前置条件: cells数组不为null
            //true -> 当前线程匹配到的桶位上还未初始化cell
            if ((a = as[(n - 1) & h]) == null) {
                //true -> 可以尝试获取锁,表示此时没有线程在操作cells数组
                if (cellsBusy == 0) {            // Try to attach new Cell
                    //新建cell节点
                    CounterCell r = new CounterCell(x); // Optimistic create
                    //条件1: true -> 双重验证,可以尝试获取锁,进入下一个判断
                    //      false -> 双重验证,有线程抢占锁了,进入下次自旋
                    //条件2: true -> 获取锁成功,进入方法体
                    //      false -> 双重验证,有线程抢占锁了,进入下次自旋
                    if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                        //是否已经创建cell
                        boolean created = false;
                        try {               // Recheck under lock
                            //再次校验cells数组是否已创建且有值、当前线程匹配到的桶位上是否为null
                            CounterCell[] rs; int m, j;
                            if ((rs = counterCells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                //将创建好的cell节点复制到对应桶位上
                                rs[j] = r;
                                //表示创建成功
                                created = true;
                            }
                        } finally {
                            //释放锁
                            cellsBusy = 0;
                        }
                        //创建成功,退出自旋
                        if (created)
                            break;
                        //当前桶位上已有cell 可能有线程抢先创建了cell,进入下次自旋
                        continue;           // Slot is now non-empty
                    }
                }
                //设置扩容意向为false
                collide = false;
            }
            //前置条件: 1、cells数组不为null
            //        2、当前桶位上已经初始化cell
            //true -> 发生了竞争
            else if (!wasUncontended)       // CAS already known to fail
                //表示将标志重置为无竞争,rehash后再尝试累加
                wasUncontended = true;      // Continue after rehash
            //前置条件: 1、cells数组不为null 2、当前桶位上已经初始化cell 3、wasUncontended == true 表示没有发生竞争
            //true -> 累加x到cell 成功
            else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                //退出自旋
                break;
            //前置条件: 1、cells数组不为null 2、当前桶位上已经初始化cell 3、wasUncontended == true 表示没有发生竞争 4、累加x到cell 失败,表示发生竞争
            //条件1: true -> cells数组发生了扩容,进入方法体
            //      false -> cells数组与临时变量的值一致,进入下一个判断
            //条件2: true -> cells数组的长度 >= 电脑cpu数量,进入方法体
            //      false -> cells数组的长度 < 电脑cpu数量,进入下一个判断
            else if (counterCells != as || n >= NCPU)
                //设置扩容意向为false,进入下次自旋
                collide = false;            // At max size or stale
            //前置条件: 1、cells数组为null 2、当前桶位上已经初始化cell 3、wasUncontended == true 表示没有发生竞争
            //        4、累加x到cell 失败,表示发生竞争 5、cells数组可扩容
            //true -> 扩容意向为false
            else if (!collide)
                //设置扩容意向为true,进入下次自旋,准备扩容
                collide = true;
            //前置条件: 1、cells数组为null 2、当前桶位上已经初始化cell 3、wasUncontended == true 表示没有发生竞争
            //        4、累加x到cell 失败,表示发生竞争 5、cells数组可扩容 6、扩容意向为true
            //true -> 获取锁成功 准备进行扩容
            else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                try {
                    if (counterCells == as) {// Expand table unless stale
                        //将cells数组扩大一倍
                        CounterCell[] rs = new CounterCell[n << 1];
                        //迁移数据
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        //更新数组引用
                        counterCells = rs;
                    }
                } finally {
                    //释放锁
                    cellsBusy = 0;
                }
                //设置扩容意向为false
                collide = false;
                //扩容后,进入下次自旋
                continue;                   // Retry with expanded table
            }
            //rehash 进入下次自旋
            h = ThreadLocalRandom.advanceProbe(h);
        }
        //前置条件: cells数组为null 表示还未初始化cells数组
        //条件1: true -> 可获取锁
        //      false -> 目前有线程竞争锁
        //条件2: true -> cells数组没有进行初始化
        //      false -> cells数组已经被其它线程初始化
        //条件3: true -> 获取到锁
        //      false -> 遇到竞争,获取锁失败
        else if (cellsBusy == 0 && counterCells == as && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
            //是否初始化完成
            boolean init = false;
            try {                           // Initialize table
                //双重判断
                if (counterCells == as) {
                    //初始化长度为2的cells数组
                    CounterCell[] rs = new CounterCell[2];
                    //将cell插入到 二选一 的桶位上
                    rs[h & 1] = new CounterCell(x);
                    //更新cells数组引用
                    counterCells = rs;
                    //表示已完成初始化
                    init = true;
                }
            } finally {
                //释放锁
                cellsBusy = 0;
            }
            //完成初始化,退出自旋
            if (init)
                break;
        }
        //前置条件: 1、cells数组为null 2、cells数组正在初始化中
        //true -> 将累加值累加到 base 中
        else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
            break;                          // Fall back on using base
    }
}

final long sumCount() {
    //as: cells数组的引用
    //a: cell节点
    CounterCell[] as = counterCells; CounterCell a;
    //baseCount引用
    long sum = baseCount;
    if (as != null) {
        //将所有cell中的value之和 + baseCount
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    //返回暂时统计出的值(不是最终的值)
    return sum;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值