ConcurrentHashMap put源码分析

类图关系

Java8-ConcurrentHashMap特点
1.使用了懒加载模式,在第一次put数据时,会执行初始化操作,初始化大小默认为16
2.使用了数组+链表+红黑树的方式存储数据
3.使用了CAS+Synchronize来并发控制put、remove操作,对于get 读操作是没有添加锁的
4.支持多线程操作,并发控制,对于同一桶进行操作需要取得锁才能访问(put, remove)
下面冲put实现来一一分析一下

ConcurrentHashMap 数据结构

重要属性

// ForwardingNode节点的哈希值
static final int MOVED = -1; // hash for forwarding nodes

// 红黑树根节点哈希值
static final int TREEBIN = -2; // hash for roots of trees

// CPU核心个数
static final int NCPU = Runtime.getRuntime().availableProcessors();

/** 控制标识符,不同的值表示不同的意义,默认值为0:
  * -1代表数组正在进行初始化
  * -N代表有N-1个线程正在进行扩容操作????
  * 正数代表下一次需要进行扩容时的阈值,也就是当前容量的0.75(负载因子)倍
  */
private transient volatile int sizeCtl;

// 数据迁移时的索引
private transient volatile int transferIndex;

// 存放数据的数组,大小始终为2的整数次幂
transient volatile Node[] table;

// 只有在扩容时才用得到,用于存储扩容后的数据,因此只有在扩容时nextTable才非空
private transient volatile Node[] nextTable;

// 
private transient volatile long baseCount;

// 数据迁移时每个线程每次迁移数据的最小步长
private static final int MIN_TRANSFER_STRIDE = 16;

// 用于计算sizeCtrl时用到的生成戳,最小为6
private static int RESIZE_STAMP_BITS = 16;

// 可以帮助扩容的最大线程数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

// 把生成戳保存在sizeCtl中的移位量
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;

重要的原子操作

// tab 中获取对应元素
 @SuppressWarnings("unchecked")
 static final  Node tabAt(Node[] tab, int i) {
      // 获取obj对象中offset偏移地址对应的object型field的值,支持volatile load语义。
      return (Node) U.getObjectVolatile(tab, ((long) i << ASHIFT) + ABASE);
 }

 // 基于 CAS 思想替换值
 static final  boolean casTabAt(Node[] tab, int i, Node c, Node v) {
      // 如果 tab 中 对应位置元素 C 相等的替换成 V
      return U.compareAndSwapObject(tab, ((long) i << ASHIFT) + ABASE, c, v);
 }

 // 设置数组元素
 static final  void setTabAt(Node[] tab, int i, Node v) {
      // 数组添加元素
      U.putObjectVolatile(tab, ((long) i << ASHIFT) + ABASE, v);
 }

ConcurrentHashMap初始化

public ConcurrentHashMap getMap() {
     return map;
}

public ConcurrentHashMap(Map m) {
    this.sizeCtl = DEFAULT_CAPACITY;
    putAll(m);
}

public ConcurrentHashMap(int initialCapacity) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException();

    // 最小容量如果大于最大容量的一半,则直接使用最大容量
    // 最小容量如果小于于最大容量的一半,则使用计算出来的容量
    int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
    this.sizeCtl = cap;
}

可以看到如果带有容量初始化涉及到容量的计算问题

/**
 * 计算容量,得到容量一定是刚好是大于 C 的 2^x 次幂
 */
private static final int tableSizeFor(int c) {
    int n = c - 1;
    // 对 n 四次位运算后得到自最高位以下全部变为 1,相当于 z^x - 1
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;

    // n < 0 时容量是 1
    // n > 0, n 大于等于最大容量的时候,使用最大容量
    // n < MAXIMUM_CAPACITY, 返回 n + 1, n + 1 是一定是 2^x
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

put 添加元素

public V put(K key, V value) {
    return putVal(key, value, false);
}

put 方法调用 putVal 方法, putVal()方法的总体思路为:

  1. hash数组是否为空,为空则先调用initTable()方法进行初始化
  2. 如果hash数组已经初始化了,则根据hash值找到对应的数组下标,如果对应节点为空,通过cas方式直接插入
  3. 如果数组已经扩容,则进行数据迁移
  4. 如果数组该位置已经有值了,则需要对该节点加锁并进行数据插入操作。此时如果该节点是链表结构,则遍历链表,插入数据;如果如果该节点是红黑树结构,则调用红黑树的插值方法插入新值
  5. 针对链表结构,如果插入新元素后,hash数组长度超过阈值,则需要调用treeifyBin()方法进行扩容或者是将链表转换为红黑树
  6. 对哈希表的元素进行计数处理
final V putVal(K key, V value, boolean onlyIfAbsent) {
  if (key == null || value == null) 
       throw new NullPointerException();

  // 对 key 的 hash 值重新计算
  int hash = spread(key.hashCode());
  int binCount = 0;
  for (Node[] tab = table; ;) {
       Node f;
       int n, i, fh;
       // 如果数组是 null 或者大小是 0 则初始化
       if (tab == null || (n = tab.length) == 0){
            // 初始化数组
            tab = initTable(); 
       }else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            // i = key 的 hash 值对数组长度取余
            // 找到 key 对应值赋值给 f, 如果对应 value 是 null,则基于 CAS 思想添加
            if (casTabAt(tab, i, null, new Node(hash, key, value, null)))
                 break;
       } else if ((fh = f.hash) == MOVED){
            // f 不为 null,且 hash 值是 -1 表示当前节点正在迁移,然后进入自旋等待,直到所有数据迁移完成
            tab = helpTransfer(tab, f);
       }else {
            V oldVal = null;
            synchronized (f) {
                 // 对象还是是同一个对象时
                 if (tabAt(tab, i) == f) {
                      // f 的 hash 值大于 0
                      if (fh >= 0) {
                           binCount = 1;
                           for (Node e = f; ; ++binCount) {
                                K ek;
                                // 找到,如果不要 CAS ,直接修改值
                                if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
                                     oldVal = e.val;
                                     if (!onlyIfAbsent)
                                          e.val = value;
                                     break;
                                }

                                // 循环到最后,不存在形同的 key 则添加到链表最后
                                Node pred = e;
                                if ((e = e.next) == null) { 
                                     pred.next = new Node(hash, key, value, null);
                                     break;
                                }
                           }
                      } else if (f instanceof TreeBin) {
                           // 添加到红黑树中
                           Node p;
                           binCount = 2;
                           if ((p = ((TreeBin) f).putTreeVal(hash, key, value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                     p.val = value;
                           }
                      }
                 }
            }

            // binCount != 0 说明上面在做链表或者红黑树操作
            if (binCount != 0) {
                 // 当前节点大于等于 8 则链表转成红黑树
                 if (binCount >= TREEIFY_THRESHOLD)
                      treeifyBin(tab, i);

                 // 如果是替换则返回原值
                 if (oldVal != null)
                      return oldVal;
                 break;
            }
       }
  }

  addCount(1L, binCount);
  return null;
}

initTable 初始化 ConcurrentHashMap 数组 table

/**
 * 初始化数组
 */
private final Node[] initTable() {
    Node[] tab;
    int sc;
    while ((tab = table) == null || tab.length == 0) {
           // 表初始化和大小调整控制符为负数,表示抢锁失败,其他线程正在执行初始化任务,
           // 暂停当前正在执行的线程对象,并执行其他线程。
           if ((sc = sizeCtl) < 0)
                Thread.yield();
           // 基于 CAS 思想,如果底层数组偏移量 SIZECTL 和 sc 值一致则将 -1 赋值给 sizeCtl,成功则进行初始化操作
           else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                     if ((tab = table) == null || tab.length == 0) {
                          // 赋值初始化默认大小,16
                          int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                          // new 一个数组
                          @SuppressWarnings("unchecked")
                          Node[] nt = (Node[]) new Node[n];
                          // 将这个数组赋值给 table,table 是 volatile 的
                          table = tab = nt;
                          // 如果 n 为 16 的话,那么这里 sc = 12
                          // 其实就是 0.75 * n
                          sc = n - (n >>> 2);
                     }
                } finally {
                     // 初始化之后 sizeCtl = 12
                     sizeCtl = sc;
                }
                break;
           }
      }

      return tab;
 }

spread()方法

// key hash 值计算
// 将散列的较高位散布(XOR)较低,也将最高位强制为0。由于该表使用2的幂次掩码,
// 所以仅在当前掩码上方的位中变化的哈希集将始终发生冲突。 (众所周知的示例是在小表中包含连续整数的Float键集。)
// 因此,我们应用了一种变换,将向下传播较高位的影响。 在速度,实用性和位扩展质量之间需要权衡。 
// 由于许多常见的哈希集已经合理分布(因此无法从扩展中受益),并且由于我们使用树来处理容器中的大量冲突集,
// 因此我们仅以最便宜的方式对一些移位后的位进行XOR,以减少系统损失, 以及合并最高位的影响,
// 否则由于表范围的限制,这些位将永远不会在索引计算中使用。
static final int spread(int h) {
      // 1、h 向右无符号右移 16 位,将高位 16 位置位 0
      // 2、第一步得到的结果和 h 进行异或操作,实际上是 h 的高位 16 位和低位 16 位进行异或操作
      // 3、和 HASH_BITS 进行 与 操作, 01111111111111111111111111111111
      return (h ^ (h >>> 16)) & HASH_BITS;
}

helpTransfer 协助数据迁移

当前节点如果是 ForwardingNode 子类表示该节点已经迁移,当前 ConcurrentHashMap 正在进行数据迁移 ,ForwardingNode 子类 hash 值是 -1 和成员变量 MOVED 相等

/**
 * 如果正在调整大小,协助扩容
 */
final Node[] helpTransfer(Node[] tab, Node f) {
    Node[] nextTab;
    int sc;
    // 数组不是 null,且 f 是ForwardingNode 子类,且 next 是数组
    if (tab != null && (f instanceof ForwardingNode) && (nextTab = ((ForwardingNode) f).nextTable) != null) {
        // 返回扩容校验标识
        int rs = resizeStamp(tab.length);
        // sizeCtl 如果处于扩容状态的话
        while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) {
             // 如果 sc 的低 16 位不等于 标识符(校验异常 sizeCtl 变化了)
             // 如果 sc == 标识符 + 1 (扩容结束了,不再有线程进行扩容)(默认第一个线程设置 sc ==rs 左移 16 位 + 2,当第一个线程结束扩容了,就会将 sc 减一。这个时候,sc 就等于 rs + 1)
             // 如果 sc == 标识符 + 65535(帮助线程数已经达到最大)
             // 如果 nextTable == null(结束扩容了)
             // 如果 transferIndex <= 0 (所有数据都已经迁移完成)
             // 结束循环 
            if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || transferIndex <= 0)
                break;
            // 抢到迁移任务则 sc + 1
            if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                transfer(tab, nextTab);
                break;
            }
        }
        return nextTab;
    }
    return table;
}

treeifyBin 链表转红黑树

treeifyBin()方法用于对数组进行扩容或者是将链表结构转换为红黑树,当一个节点的元素个数大于阈值(默认值8)时,如果此时数组长度小于MIN_TREEIFY_CAPACITY(默认值64),则对数组进行扩容,否则将该节点转换为红黑树:

/**
 * 链表转成红黑树
 *
 * tab 是 ConcurrentHashMap 的 table
 * index 当前操作节点索引
 */
private final void treeifyBin(Node[] tab, int index) {
    Node b;
    int n, sc;
    if (tab != null) {
        // 数组长度小于 64 则进行扩容,并不是转成红黑树
        if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
            tryPresize(n << 1);
        else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
            // 当前节点不是 null,且 hash 值大于等于 0 则表示当前节点没有再迁移
            synchronized (b) {
                // 乐观锁
                if (tabAt(tab, index) == b) {
                    TreeNode hd = null, tl = null;
                    for (Node e = b; e != null; e = e.next) {
                        // 以链表形式连接起来
                        TreeNode p = new TreeNode(e.hash, e.key, e.val, null, null);
                        if ((p.prev = tl) == null)
                            hd = p;
                        else
                            tl.next = p;
                        tl = p;
                    }
                    // 转成红黑树完成之后重新放入 table 中,放入表里面的是红黑树的跟节点
                    setTabAt(tab, index, new TreeBin(hd));
                }
            }
        }
    }
}

tryPresize 尝试扩容

/**
 * 尝试扩容
 */
private final void tryPresize(int size) {
    // c:size 的 1.5 倍,再加 1,再往上取最近的 2 的 n 次方。
    int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1);
    int sc;
    // sizeCtl 大于等于 0 表示还没其他线程进行扩容
    while ((sc = sizeCtl) >= 0) {
        Node[] tab = table;
        int n;
        // tab 是 null 或者数组长度是 0 表示是空,需要初始化
        if (tab == null || (n = tab.length) == 0) {
            n = (sc > c) ? sc : c;
            if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if (table == tab) {
                        @SuppressWarnings("unchecked")
                        Node[] nt = (Node[]) new Node[n];
                        table = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    // sizeCtl = 0.75 * n
                    sizeCtl = sc;
                }
            }
        } else if (c <= sc || n >= MAXIMUM_CAPACITY)
            // sc >= c 或者大于等于最大容量 则不进行扩容或者无法扩容了
            break;
        else if (tab == table) {
            // 计算出来一个标识
            int rs = resizeStamp(n);
            if (sc < 0) {
                Node[] nt;

                // 如果 sc 的低 16 位不等于 标识符(校验异常 sizeCtl 变化了)
                // 如果 sc == 标识符 + 1 (扩容结束了,不再有线程进行扩容)(默认第一个线程设置 sc ==rs 左移 16 位 + 2
                // ,当第一个线程结束扩容了,就会将 sc 减一。这个时候,sc 就等于 rs + 1)
                // 如果 sc == 标识符 + 65535(帮助线程数已经达到最大)
                // 如果 nextTable == null(结束扩容了)
                // 如果 transferIndex <= 0 (所有数据都已经迁移完成)
                // 结束循环 
                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);
            } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2))
                // 发起扩容, rs 左移十六位 + 2 
                transfer(tab, null);
        }
    }
}

transfer 数据迁移

在helpTransfer()、tryPresize()中都调用了transfer()方法。顾名思义,其核心功能就是执行数据迁移,即将数据从扩容前的数组迁移到扩容后的数组中。

/**
 * 扩容并迁移核心部分
 */
private final void transfer(Node[] tab, Node[] nextTab) {
    int n = tab.length, stride;
    // 如果是多核, 数组长度无符号右移 3 位 除 cpu 核心数量得到结果如果小 16,设置步幅为 16
    // stride 可以理解为”步长“,有 n 个位置是需要进行迁移的,
    // 将这 n 个任务分为多个任务包,每个任务包有 stride 个任务
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE;

    // nextTab 是 null,表示最开始迁移(拆分子任务迁移 nextTab != null)
    if (nextTab == null) {
        try {
            // new 一个 n 的 2 倍大小的数组,然后赋值到 nextTab
            @SuppressWarnings("unchecked")
            Node[] nt = (Node[]) new Node[n << 1];
            nextTab = nt;
        } catch (Throwable ex) {
            sizeCtl = Integer.MAX_VALUE;
            return;
        }
        // 将扩容后的数组赋值到 nextTable
        nextTable = nextTab;
        // 最新数组大小,用于控制迁移的位置
        transferIndex = n;
    }


    int nextn = nextTab.length;
    // ForwardingNode 翻译过来就是正在被迁移的 Node
    // 这个构造方法会生成一个Node,key、value 和 next 都为 null,关键是 hash 为 MOVED
    // 后面我们会看到,原数组中位置 i 处的节点完成迁移工作后,
    // 就会将位置 i 处设置为这个 ForwardingNode,用来告诉其他线程该位置已经处理过了
    // 所以它其实相当于是一个标志。
    ForwardingNode fwd = new ForwardingNode(nextTab);
    boolean advance = true;
    boolean finishing = false;
    for (int i = 0, bound = 0; ; ) {
        Node f;
        int fh;
        while (advance) {
            int nextIndex, nextBound;
            if (--i >= bound || finishing){
                // i 大于 bound 或者结束标识设置为 ture 表示拆分完成
                advance = false;
            }else if ((nextIndex = transferIndex) <= 0) {
                // 将 transferIndex 值赋给 nextIndex
                // 这里 transferIndex 一旦小于等于 0,说明原数组的所有位置都有相应的线程去处理了
                i = -1;
                advance = false;
            } else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex, nextBound = (nextIndex > stride ? nextIndex - stride : 0))) {
                // 设置需要拆分索引 TRANSFERINDEX 成功
                // 看括号中的代码,nextBound 是这次迁移任务的边界,注意,是从后往前, 其实位置是 transferIndex - stride 相当于子任务拆分是从后向前
                // 所以 bound 可以看做是数据迁移边界
                bound = nextBound;
                i = nextIndex - 1;
                advance = false;
            }
        }

        if (i < 0 || i >= n || i + n >= nextn) {
            int sc;
            if (finishing) {
                // 如果数据迁移结束,则将 nextTable 设置为null
                nextTable = null;
                // 重新赋值 table
                table = nextTab;
                // sizeCtl 设置为 n 的 3/4 - 1
                sizeCtl = (n << 1) - (n >>> 1);
                return;
            }

            // 之前我们说过,sizeCtl 在迁移前会设置为 (rs << RESIZE_STAMP_SHIFT) + 2
            // 然后,每有一个线程参与迁移就会将 sizeCtl 加 1,
            // 这里使用 CAS 操作对 sizeCtl 进行减 1,代表做完了属于自己的任务
            if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                // 设置 SIZECTL 成功
                if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                    return;
                // 都设置为 true
                finishing = advance = true;
                i = n; 
            }
        // 如果位置 i 处是空的,没有任何节点,那么放入刚刚初始化的 ForwardingNode ”空节点“
        } else if ((f = tabAt(tab, i)) == null){
           advance = casTabAt(tab, i, null, fwd);
        // 该位置处是一个 ForwardingNode,代表该位置已经迁移过了
        }else if ((fh = f.hash) == MOVED){
           advance = true; 
        }else {
            synchronized (f) {
                // 乐观锁判断
                if (tabAt(tab, i) == f) {
                    Node ln, hn;
                    // 头结点的 hash 大于 0,说明是链表的 Node 节点
                    if (fh >= 0) {
                        // 下面这一块和 Java7 中的 ConcurrentHashMap 迁移是差不多的,
                        // 需要将链表一分为二,
                        // 找到原链表中的 lastRun,然后 lastRun 及其之后的节点是一起进行迁移的
                        // lastRun 之前的节点需要进行克隆,然后分到两个链表中
                        int runBit = fh & n;
                        Node lastRun = f;
                        // 遍历链表
                        for (Node p = f.next; p != null; p = p.next) {
                            int b = p.hash & n;
                            if (b != runBit) {
                                runBit = b;
                                lastRun = p;
                            }
                        }
                        if (runBit == 0) {
                            ln = lastRun;
                            hn = null;
                        } else {
                            hn = lastRun;
                            ln = null;
                        }
                        // 链表会被分为两部分,与操作是 0 的位置不变,剩下的会变为当前位置 + n
                        for (Node p = f; p != lastRun; p = p.next) {
                            int ph = p.hash;
                            K pk = p.key;
                            V pv = p.val;
                            if ((ph & n) == 0)
                                ln = new Node(ph, pk, pv, ln);
                            else
                                hn = new Node(ph, pk, pv, hn);
                        }
                        // 其中的一个链表放在新数组的位置 i
                        setTabAt(nextTab, i, ln);
                        // 另一个链表放在新数组的位置 i+n
                        setTabAt(nextTab, i + n, hn);
                        // 将原数组该位置处设置为 fwd,代表该位置已经处理完毕,
                        // 其他线程一旦看到该位置的 hash 值为 MOVED,就不会进行迁移了
                        setTabAt(tab, i, fwd);
                        // advance 设置为 true,代表该位置已经迁移完毕,进行下一节点迁移
                        advance = true;
                    } else if (f instanceof TreeBin) {
                        // 红黑树
                        TreeBin t = (TreeBin) f;
                        TreeNode lo = null, loTail = null;
                        TreeNode hi = null, hiTail = null;
                        int lc = 0, hc = 0;
                        // 红黑树迁移按照排序顺序,从最小开始迁移
                        for (Node e = t.first; e != null; e = e.next) {
                            int h = e.hash;
                            TreeNode p = new TreeNode(h, e.key, e.val, null, null);
                            // 和链表处理方式一样,分成两部分
                            if ((h & n) == 0) {
                                if ((p.prev = loTail) == null)
                                    lo = p;
                                else
                                    loTail.next = p;
                                loTail = p;
                                ++lc;
                            } else {
                                if ((p.prev = hiTail) == null)
                                    hi = p;
                                else
                                    hiTail.next = p;
                                hiTail = p;
                                ++hc;
                            }
                        }
                        // 节点数量小于等于 6 则转成链表
                        // 节点数量大于 6,如果红黑树被分成两半则这里将 lo 设置为红黑树, 如果没有被分成凉拌则相当于所有元素位置不变
                        ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : (hc != 0) ? new TreeBin(lo) : t;
                        hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : (lc != 0) ? new TreeBin(hi) : t;
                        // 将红黑树第一段放到新数组中
                        setTabAt(nextTab, i, ln);
                        // 将红黑树第二段放到新数组中
                        setTabAt(nextTab, i + n, hn);
                        // 将原数组该位置处设置为 fwd,代表该位置已经处理完毕,
                        // 其他线程一旦看到该位置的 hash 值为 MOVED,就不会进行迁移了
                        setTabAt(tab, i, fwd);
                        // advance 设置为 true,代表该位置已经迁移完毕,进行下一节点迁移
                        advance = true;
                    }
                }
            }
        }
    }
}

addCount 计数器盒子

addCount 方法做了 2 件事情:

  1. 对 table 的长度加一。无论是通过修改 baseCount,还是通过使用 CounterCell。当 CounterCell 被初始化了,就优先使用他,不再使用 baseCount。
  2. 检查 table 是否需要扩容,或者是否正在扩容。如果需要扩容,就调用扩容方法,如果正在扩容,就帮助其扩容。

有几个要点注意:

  1. 第一次调用扩容方法前,sizeCtl 的低 16 位是加 2 的,不是加一。所以 sc == rs + 1 的判断是表示是否完成任务了。因为完成扩容后,sizeCtl == rs + 1。
  2. 扩容线程最大数量是 65535,是由于低 16 位的位数限制。
  3. 这里也是可以帮助扩容的,类似 helpTransfer 方法。

该方法主要逻辑:x 参数表示的此次需要对表中元素的个数加几。check 参数表示是否需要进行扩容检查,大于等于0 需要进行检查,而我们的 putVal 方法的 binCount 参数最小也是 0 ,因此,每次添加元素都会进行检查。(除非是覆盖操作)

  1. 判断计数盒子属性是否是空,如果是空,就尝试修改 baseCount 变量,对该变量进行加 X。
  2. 如果计数盒子不是空,或者修改 baseCount 变量失败了,则放弃对 baseCount 进行操作。
  3. 如果计数盒子是 null 或者计数盒子的 length 是 0,或者随机取一个位置取于数组长度是 null,那么就对刚刚的元素进行 CAS 赋值。
  4. 如果赋值失败,或者满足上面的条件,则调用 fullAddCount 方法重新死循环插入。
  5. 这里如果操作 baseCount 失败了(或者计数盒子不是 Null),且对计数盒子赋值成功,那么就检查 check 变量,如果该变量小于等于 1. 直接结束。否则,计算一下 count 变量。
  6. 如果 check 大于等于 0 ,说明需要对是否扩容进行检查。
  7. 如果 map 的 size 大于 sizeCtl(扩容阈值),且 table 的长度小于 1 << 30,那么就进行扩容。
  8. 根据 length 得到一个标识符,然后,判断 sizeCtl 状态,如果小于 0 ,说明要么在初始化,要么在扩容。
  9. 如果正在扩容,那么就校验一下数据是否变化了(具体可以看上面代码的注释)。如果检验数据不通过,break。
  10. 如果校验数据通过了,那么将 sizeCtl 加一,表示多了一个线程帮助扩容。然后进行扩容。
  11. 如果没有在扩容,但是需要扩容。那么就将 sizeCtl 更新,赋值为标识符左移 16 位 —— 一个负数。然后加 2。 表示,已经有一个线程开始扩容了。然后进行扩容。然后再次更新 count,看看是否还需要扩容。
/**
 * 添加计数,如果表太小且尚未调整大小,则启动扩容。 
 * 如果已经调整大小,则在工作可用时帮助执行转移。 
 * 转移后重新检查占用率,以查看是否由于调整大小落后于其他调整而需要重新调整大小。
 * 
 * 从 putVal 传入的参数是 1, binCount,binCount 默认是0,只有 hash 冲突了才会大于 1.且他的大小是链表的长度(如果不是红黑数结构的话)。
 */
private final void addCount(long x, int check) {
    CounterCell[] as;
    long b, s;

    // 如果CounterCell数组中存在值,则说明有 更新值没有存储到baseCount 中
    // 并且CAS 中存储的baseCount值 不一样,需要把差量数据全量插入
    // 如果相同则 更新 baseCount 的值 = baseCount + x
    if ((as = counterCells) != null || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        CounterCell a;
        long v;
        int m;
        boolean uncontended = true;
        // 高并发下 CAS 失败会执行 fullAddCount 方法
        if (as == null || (m = as.length - 1) < 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
            fullAddCount(x, uncontended);
            return;
        }

        // 小于等于 1 表示没有新增元素只是替换,所以计数不需要增加
        if (check <= 1)
            return;
        // 大于 1,且存在并发
        s = sumCount();
    }

    // 如果需要检查,检查是否需要扩容,在 putVal 方法调用时,默认就是要检查的。
    if (check >= 0) {
        Node[] tab, nt;
        int n, sc;
        // 如果map.size() 大于 sizeCtl(达到扩容阈值需要扩容) 且
        // table 不是空;且 table 的长度小于 1 << 30。(可以扩容)
        while (s >= (long) (sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) {
            // 根据 length 得到一个扩容标识
            int rs = resizeStamp(n);
            // 如果正在扩容
            if (sc < 0) {
                // 如果 sc 的低 16 位不等于 标识符(校验异常 sizeCtl 变化了)
                // 如果 sc == 标识符 + 1 (扩容结束了,不再有线程进行扩容)(默认第一个线程设置 sc ==rs 左移 16 位 + 2,当第一个线程结束扩容了,就会将 sc 减一。这个时候,sc 就等于 rs + 1)
                // 如果 sc == 标识符 + 65535(帮助线程数已经达到最大)
                // 如果 nextTable == null(结束扩容了)
                // 如果 transferIndex <= 0 (所有数据都已经迁移完成)
                // 结束循环 
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0)
                    break;

                // 如果可以帮助扩容,那么将 sc 加 1. 表示多了一个线程在帮助扩容
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)){
                // 如果不在扩容,将 sc 更新:标识符左移 16 位 然后 + 2. 也就是变成一个负数。高 16 位是标识符,低 16 位初始是 2. 开始扩容
                transfer(tab, null);
            }
            s = sumCount();
        }
    }
}

fullAddCount

注解@sun.misc.Contended用于解决伪共享问题。所谓伪共享,即是在同一缓存行(CPU缓存的基本单位)中存储了多个变量,当其中一个变量被修改时,就会影响到同一缓存行内的其他变量,导致它们也要跟着被标记为失效,其他变量的缓存命中率将会受到影响。解决伪共享问题的方法一般是对该变量填充一些无意义的占位数据,从而使它独享一个缓存行。

ConcurrentHashMap的计数设计与LongAdder类似。在一个低并发的情况下,就只是简单地使用CAS操作来对baseCount进行更新,但只要这个CAS操作失败一次,就代表有多个线程正在竞争,那么就转而使用CounterCell数组进行计数,数组内的每个ConuterCell都是一个独立的计数单元。

每个线程都会通过ThreadLocalRandom.getProbe() & m寻址找到属于它的CounterCell,然后进行计数。ThreadLocalRandom是一个线程私有的伪随机数生成器,每个线程的probe都是不同的(这点基于ThreadLocalRandom的内部实现,它在内部维护了一个probeGenerator,这是一个类型为AtomicInteger的静态常量,每当初始化一个ThreadLocalRandom时probeGenerator都会先自增一个常量然后返回的整数即为当前线程的probe,probe变量被维护在Thread对象中),可以认为每个线程的probe就是它在CounterCell数组中的hash code。

这种方法将竞争数据按照线程的粒度进行分离,相比所有竞争线程对一个共享变量使用CAS不断尝试在性能上要效率多了,这也是为什么在高并发环境下LongAdder要优于AtomicInteger的原因。

fullAddCount()函数根据当前线程的probe寻找对应的CounterCell进行计数,如果CounterCell数组未被初始化,则初始化CounterCell数组和CounterCell。该函数的实现与Striped64类(LongAdder的父类)的longAccumulate()函数是一样的,把CounterCell数组当成一个散列表,每个线程的probe就是hash code,散列函数也仅仅是简单的(n - 1) & probe。

CounterCell数组的大小永远是一个2的n次方,初始容量为2,每次扩容的新容量都是之前容量乘以二,处于性能考虑,它的最大容量上限是机器的CPU数量。

所以说CounterCell数组的碰撞冲突是很严重的,因为它的bucket基数太小了。而发生碰撞就代表着一个CounterCell会被多个线程竞争,为了解决这个问题,Doug Lea使用无限循环加上CAS来模拟出一个自旋锁来保证线程安全,自旋锁的实现基于一个被volatile修饰的整数变量,该变量只会有两种状态:0和1,当它被设置为0时表示没有加锁,当它被设置为1时表示已被其他线程加锁。这个自旋锁用于保护初始化CounterCell、初始化CounterCell数组以及对CounterCell数组进行扩容时的安全。

CounterCell更新计数是依赖于CAS的,每次循环都会尝试通过CAS进行更新,如果成功就退出无限循环,否则就调用ThreadLocalRandom.advanceProbe()函数为当前线程更新probe,然后重新开始循环,以期望下一次寻址到的CounterCell没有被其他线程竞争。

如果连着两次CAS更新都没有成功,那么会对CounterCell数组进行一次扩容,这个扩容操作只会在当前循环中触发一次,而且只能在容量小于上限时触发。

fullAddCount()函数的主要流程如下:

  1. 首先检查当前线程有没有初始化过ThreadLocalRandom,如果没有则进行初始化。ThreadLocalRandom负责更新线程的probe,而probe又是在数组中进行寻址的关键。
  2. 检查CounterCell数组是否已经初始化,如果已初始化,那么就根据probe找到对应的CounterCell。
  3. 如果这个CounterCell等于null,需要先初始化CounterCell,通过把计数增量传入构造函数,所以初始化只要成功就说明更新计数已经完成了。初始化的过程需要获取自旋锁。
  4. 如果不为null,就按上文所说的逻辑对CounterCell实施更新计数。
  5. CounterCell数组未被初始化,尝试获取自旋锁,进行初始化。数组初始化的过程会附带初始化一个CounterCell来记录计数增量,所以只要初始化成功就表示更新计数完成。
  6. 如果自旋锁被其他线程占用,无法进行数组的初始化,只好通过CAS更新baseCount。
private final void fullAddCount(long x, boolean wasUncontended) {
    int h;
    // 当前线程的probe等于0,证明该线程的ThreadLocalRandom还未被初始化
    // 以及当前线程是第一次进入该函数
    if ((h = ThreadLocalRandom.getProbe()) == 0) {
        // 第一次进来则进行初始化
        ThreadLocalRandom.localInit(); 
        // 重新获取探测值
        h = ThreadLocalRandom.getProbe();
        // 未竞争标志
        wasUncontended = true;
    }
    // 冲突标志
    boolean collide = false; 
    for (; ; ) {
        CounterCell[] as;
        CounterCell a;
        int n;
        long v;
        // CounterCell数组已初始化
        if ((as = counterCells) != null && (n = as.length) > 0) {
            // 如果寻址到的Cell为空,那么创建一个新的Cell,说明当前线程第一次失败
            if ((a = as[(n - 1) & h]) == null) {
                // cellsBusy是一个只有0和1两个状态的volatile整数
                // 它被当做一个自旋锁,0代表无锁,1代表加锁
                if (cellsBusy == 0) {            // Try to attach new Cell
                    // 将传入的x作为初始值创建一个新的CounterCell
                    CounterCell r = new CounterCell(x); // Optimistic create
                    // 加锁成功,声明Cell是否创建成功的标志
                    if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                        boolean created = false;
                        try {               // Recheck under lock
                            CounterCell[] rs;
                            int m, j;
                            // 再次检查CounterCell数组是否不为空
                            // 并且寻址到的Cell为空
                            // 其实就是乐观锁检查
                            if ((rs = counterCells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) {
                                // 将之前创建的新Cell放入数组
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            // 释放锁
                            cellsBusy = 0;
                        }
                        // 创建成功调处循环
                        // 因为新Cell的初始值就是传入的增量,所以计数已经完毕了
                        if (created)
                            break;

                        // 如果未成功
                        // 代表as[(n - 1) & h]这个位置的Cell已经被其他线程设置
                        // 那么就从循环头重新开始
                        continue;           // Slot is now non-empty
                    }
                }
                collide = false;
            } else if (!wasUncontended)       // CAS already known to fail
                // as[(n - 1) & h]非空
                // 在addCount()函数中通过CAS更新当前线程的Cell进行计数失败
                // 会传入wasUncontended = false,代表已经有其他线程进行竞争
                // 设置未竞争标志,之后会重新计算probe,然后重新执行循环
                wasUncontended = true; 
            // 尝试进行计数,如果成功,那么就退出循环
            else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                break;
            // 尝试更新失败,检查counterCell数组是否已经扩容
            // 或者容量达到最大值(CPU的数量)
            else if (counterCells != as || n >= NCPU)
                // 设置冲突标志,防止跳入下面的扩容分支
                // 之后会重新计算probe
                collide = false;

            // 设置冲突标志,重新执行循环
            // 如果下次循环执行到该分支,并且冲突标志仍然为true
            // 那么会跳过该分支,到下一个分支进行扩容
            else if (!collide)
                collide = true;

            // 尝试加锁,然后对counterCells数组进行扩容
            else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                try {
                    // 检查是否已被扩容
                    if (counterCells == as) {// Expand table unless stale
                        // 新数组容量为之前的1倍
                        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
            }
            // 为当前线程重新计算probe
            h = ThreadLocalRandom.advanceProbe(h);
        } else if (cellsBusy == 0 && counterCells == as && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
           // CounterCell数组未初始化,尝试获取自旋锁,然后进行初始化
            boolean init = false;
            try {                           // Initialize table
                if (counterCells == as) {
                    // 初始化CounterCell数组,初始容量为2
                    CounterCell[] rs = new CounterCell[2];
                    rs[h & 1] = new CounterCell(x);
                    counterCells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            // 初始化CounterCell数组成功,退出循环   
            if (init)
                break;
        } else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
            // 如果自旋锁被占用,则只好尝试更新baseCount,更新成功退出否则自旋
            break;
    }
}

这部分使用了内部类CounterCell

@sun.misc.Contended static final class CounterCell {
    volatile long value;
    CounterCell(long x) { value = x; }
}

ConcurrentHashMap 数据迁移过程中使用的 ForwardingNode 对象

/**
 * Node 子类
 */
static final class ForwardingNode extends Node {
    final Node[] nextTable;

    // 将节点hashCode 设置为 -1,tab 赋值给 nextTable
    ForwardingNode(Node[] tab) {
        super(MOVED, null, null, null);
        this.nextTable = tab;
    }

    Node find(int h, Object k) {
        // loop to avoid arbitrarily deep recursion on forwarding nodes
        outer:
        for (Node[] tab = nextTable; ; ) {
            Node e;
            int n;
            // 不存在返回 null
            if (k == null || tab == null || (n = tab.length) == 0 || (e = tabAt(tab, (n - 1) & h)) == null)
                return null;

            // 循环找
            for (; ; ) {
                int eh;
                K ek;
                // 节点 hash 值相等,key 指向地址相同,且 key 非 null 且 hashCode 相等
                if ((eh = e.hash) == h && ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;

                // 如果 hash 值小于 0
                if (eh < 0) {
                     // 如果 e 是 ForwardingNode 子类,则修改 tab 指向,重新循环
                    if (e instanceof ForwardingNode) {
                        tab = ((ForwardingNode) e).nextTable;
                        continue outer;
                    } else
                        // 递归查找
                        return e.find(h, k);
                }
                // 最终没找到,则返回 null
                if ((e = e.next) == null)
                    return null;
            }
        }
    }
}

ConcurrentHashMap 中红黑树

static final class TreeBin extends Node {
    TreeNode root;
    volatile TreeNode first;
    volatile Thread waiter;
    volatile int lockState;
    // values for lockState
    static final int WRITER = 1; // set while holding write lock
    static final int WAITER = 2; // set when waiting for write lock
    static final int READER = 4; // increment value for setting read lock

    //
    static int tieBreakOrder(Object a, Object b) {
        int d;
        if (a == null || b == null || (d = a.getClass().getName().compareTo(b.getClass().getName())) == 0)
            d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1);
        return d;
    }

    /**
     * Creates bin with initial set of nodes headed by b.
     */
    TreeBin(TreeNode b) {
        super(TREEBIN, null, null, null);
        // 链表头,变成红黑树之后,还维持这之前链表的关系
        this.first = b;
        TreeNode r = null;
        for (TreeNode x = b, next; x != null; x = next) {
            next = (TreeNode) x.next;
            x.left = x.right = null;
            // 树是空的,则第一个节点是根节点
            if (r == null) {
                x.parent = null;
                x.red = false;
                r = x;
            } else {
                K k = x.key;
                int h = x.hash;
                Class kc = null;
                for (TreeNode p = r; ; ) {
                    int dir, ph;
                    K pk = p.key;
                    if ((ph = p.hash) > h)
                        dir = -1;
                    else if (ph < h)
                        dir = 1;
                    else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0)
                        dir = tieBreakOrder(k, pk);

                    TreeNode xp = p;
                    if ((p = (dir <= 0) ? p.left : p.right) == null){
                        // 设置父节点
                        x.parent = xp;

                        // 设置子节点
                        if (dir <= 0)
                            xp.left = x;
                        else
                            xp.right = x;

                        // 做红黑树插入后平衡
                        r = balanceInsertion(r, x);
                        break;
                    }
                }
            }
        }
        // 最后将根节点设置到 root
        this.root = r;
        assert checkInvariants(root);
    }

    // 获取用于树重组的写锁。
    private final void lockRoot() {
        if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
            contendedLock(); // offload to separate method
    }

    /**
     * 释放用于树重组的写锁。
     */
    private final void unlockRoot() {
        lockState = 0;
    }

    /**
     * 可能阻止等待root用户锁定。
     */
    private final void contendedLock() {
        boolean waiting = false;
        for (int s; ; ) {
            if (((s = lockState) & ~WAITER) == 0) {
                if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
                    if (waiting)
                        waiter = null;
                    return;
                }
            } else if ((s & WAITER) == 0) {
                if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
                    waiting = true;
                    waiter = Thread.currentThread();
                }
            } else if (waiting)
                LockSupport.park(this);
        }
    }

    /**
     * 返回匹配的节点;如果没有,则返回null。 尝试从根目录使用树比较进行搜索,但是在锁不可用时继续线性搜索。
     */
    final Node find(int h, Object k) {
        if (k != null) {
            for (Node e = first; e != null; ) {
                int s;
                K ek;
                // s = 0 或 4
                if (((s = lockState) & (WAITER | WRITER)) != 0) {
                     // 线性查找,按照链表查询
                    if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                    e = e.next;
                } else if (U.compareAndSwapInt(this, LOCKSTATE, s, s + READER)) {
                     // 否则按照红黑树查找
                    TreeNode r, p;
                    try {
                        p = ((r = root) == null ? null : r.findTreeNode(h, k, null));
                    } finally {
                        Thread w;
                        if (U.getAndAddInt(this, LOCKSTATE, -READER) == (READER | WAITER) && (w = waiter) != null)
                            LockSupport.unpark(w);
                    }
                    return p;
                }
            }
        }
        return null;
    }

    /**
     * 红黑树添加元素
     */
    final TreeNode putTreeVal(int h, K k, V v) {
        Class kc = null;
        boolean searched = false;
        for (TreeNode p = root; ; ) {
            int dir, ph;
            K pk;
            // 根节点是是 null,则插入节点就是根节点
            if (p == null) {
                first = root = new TreeNode(h, k, v, null, null);
                break;
            } else if ((ph = p.hash) > h)
                // 当前节点hash 值大于插入节点 hash
                dir = -1;
            else if (ph < h)
                // 当前节点hash 值小于插入节点 hash
                dir = 1;
            else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                // hash 值相等,key 指向地址相等或者key 的 值相等返回 p 节点, 节点值变更不会引起红黑树失衡所以不需要调整,这里相当于找到节点返回外面进行赋值处理
                return p;
            else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) {
                //  hash 值相等个,key 不一致情况:kc 是 null, 且key 类是也 null 或者按照 key 的类 kc 进行 k 和 pk 的比较如果相等表示找到
                if (!searched) {
                     // 如果这第一次搜索到
                    TreeNode q, ch;
                    searched = true;
                    // 当前节点左节点非 null 且 左子树找到对应节点 或者 右节点非 null 且右节点找到则返回 q
                    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;
                }
                // 左右子树都没找到比较k 和 pk
                dir = tieBreakOrder(k, pk);
            }

            TreeNode xp = p;
            // dir 小于等于 0,则左子树找,大于 0 右子树找直到找到叶子节点
            if ((p = (dir <= 0) ? p.left : p.right) == null) {
                TreeNode x, f = first;
                // 将当前节点的父节点设置为 xp, 将当前节点设置为链表第一个元素,之前第一个元素设置为当前节点的下一个节点
                first = x = new TreeNode(h, k, v, f, xp);

                if (f != null)
                    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;
    }

    /**
     * 删除节点
     * p 需要删除的节点
     */
    final boolean removeTreeNode(TreeNode p) {
        // 链表下一节点
        TreeNode next = (TreeNode) p.next;
        // 链表前一节点
        TreeNode pred = p.prev;

        // 删除节点是链表头,则将下一元素设置为链表头
        TreeNode r, rl;
        if (pred == null)
            first = next;
        else
            // 不是链表头则修改前节点的的next 指向
            pred.next = next;

        // 下一节点非 null 则将下一节点的前节点指向前前节点
        if (next != null)
            next.prev = pred;

        // 删除之后,如果链表空了,表示红黑树也空了,所以直接设置为 null
        if (first == null) {
            root = null;
            return true;
        }

        // 空树,或者根节点右孩子为 null,或者右孩子的左孩子为 null 直接返回
        if ((r = root) == null || r.right == null || (rl = r.left) == null || rl.left == null)
            return true;

        // 红黑树加锁
        lockRoot();
        try {
            TreeNode replacement;
            TreeNode pl = p.left;
            TreeNode pr = p.right;
            // 删除节点的左右孩子都在
            if (pl != null && pr != null) {
                TreeNode s = pr, sl;

                // 寻找后继节点
                while ((sl = s.left) != null)
                    s = sl;

                // 这里开始是交换后继节点和删除节点
                // 将后继节点的颜色设置删除节点颜色,将删除节点设置为后继节点颜色
                boolean c = s.red;
                s.red = p.red;
                p.red = c; // swap colors

                TreeNode sr = s.right;
                TreeNode pp = p.parent;
                // 后继节点是删除节点的右孩子
                if (s == pr) { // p was s's direct parent
                    // 将 p 的右孩子设置为 p 的父节点,将后继节点的右孩子设置为 p
                    p.parent = s;
                    s.right = p;
                } else {
                     // p 不是后继节点的直接父节点
                    TreeNode sp = s.parent;
                    // 后继节点的存在父节点
                    if ((p.parent = sp) != null) {
                        // 后继节点是左孩子则将 p 设置为左孩子
                        if (s == sp.left)
                            sp.left = p;
                        else
                            // 后继节点是右孩子则将 p 设置为右孩子
                            sp.right = p;
                    }

                    // p 节点右孩子非 null 则将拼接垫右孩子父节点设置为后继节点
                    if ((s.right = pr) != null)
                        pr.parent = s;
                }
                // 将 p 节点左孩子设置为 null,因为此时后继节点的左孩子一定是 null
                p.left = null;
                // 将后继节点右孩子设置为 p 节点右孩子,如果存在右孩子,则讲右孩子的父节点设置为 p
                if ((p.right = sr) != null)
                    sr.parent = p;

                // 将后继节点的左孩子设置为 p 节点左孩子,如果存在左孩子则将 p 节点原来左孩子的父节点设置为后继节点
                if ((s.left = pl) != null)
                    pl.parent = s;

                // 将 p 节点原父节点设置为后继节点的父节点,如果是 null 则将后继节点设置为红黑树的根
                if ((s.parent = pp) == null)
                    r = s;
                else if (p == pp.left)
                    // p 是左孩子,则将后继节点设置父 p 原父节点的左孩子
                    pp.left = s;
                else
                    // p 是右孩子,则将后继节点设置父 p 原父节点的右孩子
                    pp.right = s;

                // 后继节点不是叶子节点,右孩子非 null ,将其设置为替代节点,否则设置为 p
                if (sr != null)
                    replacement = sr;
                else
                    // 后继节点是叶子节点,这里 p 和后继节点已经交换完毕,所以 p 变成叶子节点可以直接删除
                    replacement = p;
            } else if (pl != null)
                // p 节点只有左孩子,则左孩子是替代节点
                replacement = pl;
            else if (pr != null)
                // p 节点之后右孩子,则右孩子是替代节点
                replacement = pr;
            else
                // 没有左右孩子,则 P 是叶子节点或者根节点,直接删除即可,所以 P 是替代节点
                replacement = p;

            // 替代节点不是删除节点
            if (replacement != p) {
                // 将替 p 的父节点设置为替代节点的父节点
                TreeNode pp = replacement.parent = p.parent;
                // p 的父节点是 null,则将替代节点设置为根借贷
                // p 是左孩子则将替代节点设置为左孩子
                // p 是右孩子则将替代节点设置为右孩子
                if (pp == null)
                    r = replacement;
                else if (p == pp.left)
                    pp.left = replacement;
                else
                    pp.right = replacement;
                // 将 p 的左孩子,右孩子和父节点设置为 null
                p.left = p.right = p.parent = null;
            }

            // p 节点是红色节点则则直接删除,不影响黑节点数量
            // p 节点是黑色节点需要删除后平衡
            root = (p.red) ? r : balanceDeletion(r, replacement);

            // p 本身是是替代节点,即是叶子节点或者根节点,将父节点指向自己的左孩子或者右孩子设置为 null
            if (p == replacement) {  // detach pointers
                TreeNode pp;
                if ((pp = p.parent) != null) {
                    if (p == pp.left)
                        pp.left = null;
                    else if (p == pp.right)
                        pp.right = null;
                    p.parent = null;
                }
            }
        } finally {
            // 释放红黑树的锁
            unlockRoot();
        }
        assert checkInvariants(root);
        return false;
    }

    // 左旋
    static  TreeNode rotateLeft(TreeNode root, TreeNode p) {
        TreeNode r, pp, rl;
        // 右旋右孩子不能为空
        if (p != null && (r = p.right) != null) {
            // 将 p 右孩子的左孩子设置为 p 的右孩子且赋值给 rl,如果 rl 非null,则将 rl 的父节点设置为 p 节点
            if ((rl = p.right = r.left) != null)
                rl.parent = p;

            // 将 p 的父节点设置为,p 右孩子的父节点, 如果父节点不存在说明是根节点,则将 p 的右孩子颜色设置为黑色
            if ((pp = r.parent = p.parent) == null)
                // 将 p 的右孩子设置为根
                (root = r).red = false;
            else if (pp.left == p)
                // p 节点是左孩子,则将 p 的右孩子设置为 p 的父节点的左孩子
                pp.left = r;
            else
                // p 节点是右孩子,则将 p 的右孩子设置为 p 的父节点的右孩子
                pp.right = r;

            // 将 P 设置为右孩子的左节点
            r.left = p;
            // 将 p 的右孩子设置为 P 的父节点
            p.parent = r;
        }
        return root;
    }

    // 右旋
    static  TreeNode rotateRight(TreeNode root, TreeNode p) {
        TreeNode l, pp, lr;
        // p 节点左孩子必须非空才能右旋
        if (p != null && (l = p.left) != null) {
            // 将 p 节点的左孩子的右孩子赋值给 p 的左节点
            if ((lr = p.left = l.right) != null)
                // 将 p 节点的左孩子的右孩子父节点设置为 p
                lr.parent = p;

            // 将 p 的父节点设置给 左孩
            if ((pp = l.parent = p.parent) == null)
                // p 是根节点的情况,将左孩子设置为根,颜色设置为黑色
                (root = l).red = false;
            else if (pp.right == p)
                // p 是右孩子,则将 p 的左孩子设置为 p 的父节点的右孩子
                pp.right = l;
            else
                // p 是左孩子,则将 p 的左孩子设置为 p 的父节点的左孩子
                pp.left = l;

            // 将 P 设置为左孩子的右孩子
            l.right = p;
            // 将 p 的左孩子变为 p 的父节点
            p.parent = l;
        }
        return root;
    }

    /**
     * 红黑树插入后平衡
     * root 是红黑树的根
     * x 是插入节点
     */
    static  TreeNode balanceInsertion(TreeNode root, TreeNode x) {
        x.red = true;
        for (TreeNode xp, xpp, xppl, xppr; ; ) {
            // x 是根节点,颜色设置为黑色,跳出循环
            if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            // 父节点是黑色,且父节点是根节点,不用处理
            } else if (!xp.red || (xpp = xp.parent) == null)
                return root;

            // 父节点是左孩子
            if (xp == (xppl = xpp.left)) {
                // 父节点是左孩子,叔叔节点是红节点
                if ((xppr = xpp.right) != null && xppr.red) {
                    // 将叔叔节点设置为黑色
                    xppr.red = false;
                    // 将父节点设置为黑色
                    xp.red = false;
                    // 将祖父节点设置为红色
                    xpp.red = true;
                    // 把祖父节点当做插入节点进行处理
                    x = xpp;
                } else {
                    // 插入节点是右孩子,叔叔节点是黑节点或者不存在
                    if (x == xp.right) {
                        // 先对父节点左旋,然后把父节点当做插入节点处理
                        root = rotateLeft(root, x = xp);
                        // 获取插入节点的祖父节点
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }

                    // 如果插入节点是左孩子,这里只是对祖父节点右旋,下次循环依然是对 插入节点进行处理
                    if (xp != null) {
                        // 将原节点设置黑色
                        xp.red = false;
                        // 祖父节点存在
                        if (xpp != null) {
                            // 将祖父节点设置为红色
                            xpp.red = true;
                            // 然后对祖父节点右旋
                            root = rotateRight(root, xpp);
                        }
                    }
                }
            } else {
                // 父节点是右孩子,
                // 叔叔节点存在,且是红节点
                if (xppl != null && xppl.red) {
                    // 将叔叔节点变为黑节点
                    xppl.red = false;
                    // 父节点设置黑节点
                    xp.red = false;
                    // 富足节点变为哄节点
                    xpp.red = true;
                    // 将祖父节点变为下次循环需要调整的节点
                    x = xpp;
                } else {
                    // 插入节点是左孩子,叔叔节点是黑节点或者不存在
                    if (x == xp.left) {
                        // 对父节点右旋,右旋之后插入节点变为父节点
                        root = rotateRight(root, x = xp);
                        // 这里 x 变为之前的父节点,赋值祖父节点,下次循环对父节点处理
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    // 插入节点的父节点非 null,下次循环记录处理对插入节点处理
                    if (xp != null) {
                        // 插入节点的父节点设置为黑节点
                        xp.red = false;
                        // 祖父节点存在
                        if (xpp != null) {
                            // 祖父节点变为红色
                            xpp.red = true;
                            // 对祖父节点进行左旋
                            root = rotateLeft(root, xpp);
                        }
                    }
                }
            }
        }
    }

    /**
     * 删除节点后红黑树自平衡
     * root 红黑树的根
     * x 删除节点的替代节点
     */
    static  TreeNode balanceDeletion(TreeNode root, TreeNode x) {
        for (TreeNode xp, xpl, xpr; ; ) {
            // 替代节点是根节点,则不需要处理
            if (x == null || x == root)
                return root;
            // 替代节点不存在父节点也不是根节点,则是已经删除掉的节点不需要处理
            else if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            } else if (x.red) {
                // 替代节点是红节点,则只需要把替代节点变为黑节点即可
                x.red = false;
                return root;
            } else if ((xpl = xp.left) == x) {
                // 替代节点的父节点是左孩子,叔叔节点存在且是红节点
                if ((xpr = xp.right) != null && xpr.red) {
                    // 将叔叔节点和父节点设置黑节点
                    // 然后对父节点进行左旋
                    xpr.red = false;
                    xp.red = true;
                    root = rotateLeft(root, xp);
                    // 将x 节点左旋之后的父节点的右孩子赋值 xpr
                    xpr = (xp = x.parent) == null ? null : xp.right;
                }
                // 兄弟节点不存在则将父节点当做插入节点进行处理
                if (xpr == null)
                    x = xp;
                else {
                    TreeNode sl = xpr.left, sr = xpr.right;
                    if ((sr == null || !sr.red) && (sl == null || !sl.red)) {
                        // 兄弟节点的左右孩子都存在且都是黑色节点
                        // 兄弟节点的左右节点存在一个且是黑色节点
                        // 兄弟节点不存在左右孩子
                        // 将兄弟节点的右孩子设置为红节点
                        // 将父节点当做插入节点当做插入节点处理
                        xpr.red = true;
                        x = xp;
                    } else {
                        // 兄弟节点存在左右孩子至少一个是红节点
                        if (sr == null || !sr.red) {
                            // 兄弟节点的右孩子不存在或者是黑色节点
                            // 兄弟节点左孩子非 null 则设置为黑节点
                            if (sl != null)
                                sl.red = false;
                            // 将兄弟节点右节点设置为红色节点
                            xpr.red = true;
                            // 对兄弟节点右旋
                            root = rotateRight(root, xpr);
                            // 旋转后小红心获取兄弟节点
                            xpr = (xp = x.parent) == null ?  null : xp.right;
                        }
                        // 存在兄弟节点
                        if (xpr != null) {
                            // 想父节点颜色设置为兄弟节点颜色
                            xpr.red = (xp == null) ? false : xp.red;
                            // 兄弟节点右孩子存在则设置为黑色节点
                            if ((sr = xpr.right) != null)
                                sr.red = false;
                        }
                        // 父节点存在,将父节点设置黑色,然后对父节点左旋
                        if (xp != null) {
                            xp.red = false;
                            root = rotateLeft(root, xp);
                        }

                        x = root;
                    }
                }
            } else { // symmetric
                // 替代节点是右孩子
                // 叔叔节点存在且是红色节点
                if (xpl != null && xpl.red) {
                    // 将叔叔节点设为黑节点
                    // 父节点设为红节点
                    // 对父节点右旋
                    xpl.red = false;
                    xp.red = true;
                    root = rotateRight(root, xp);
                    // 旋转之后将父节点的左孩子重新赋值
                    xpl = (xp = x.parent) == null ? null : xp.left;
                }
                if (xpl == null)
                    // 叔叔节点不存在则将父节点当错插入节点进行处理
                    x = xp;
                else {
                    TreeNode sl = xpl.left, sr = xpl.right;
                    if ((sl == null || !sl.red) && (sr == null || !sr.red)) {
                        // 兄弟节点的左右孩子都存在且都是黑色节点
                        // 兄弟节点的左右节点存在一个且是黑色节点
                        // 兄弟节点不存在左右孩子
                        // 将兄弟节点的右孩子设置为红节点
                        // 将父节点当做插入节点当做插入节点处理
                        xpl.red = true;
                        x = xp;
                    } else {
                        // 兄弟节点左孩子不存在或者是黑色节点
                        if (sl == null || !sl.red) {
                            // 左孩子存在则将左孩子设置为黑节点
                            if (sr != null)
                                sr.red = false;
                            // 兄弟节点设置为红节点
                            xpl.red = true;
                            // 对兄弟节点左旋
                            root = rotateLeft(root, xpl);
                            xpl = (xp = x.parent) == null ? null : xp.left;
                        }
                        // 兄弟节点不存在
                        if (xpl != null) {
                            // 父节点不存在则设置为黑节点
                            xpl.red = (xp == null) ? false : xp.red;
                            // 兄弟节点左孩子存在设置为黑节点
                            if ((sl = xpl.left) != null)
                                sl.red = false;
                        }
                        // 父节点存在设置为黑色节点,对父节点右旋
                        if (xp != null) {
                            xp.red = false;
                            root = rotateRight(root, xp);
                        }

                        x = root;
                    }
                }
            }
        }
    }

    /**
     * Recursive invariant check
     */
    static  boolean checkInvariants(TreeNode t) {
        TreeNode tp = t.parent, tl = t.left, tr = t.right,
                tb = t.prev, tn = (TreeNode) t.next;
        if (tb != null && tb.next != t)
            return false;
        if (tn != null && tn.prev != t)
            return false;
        if (tp != null && t != tp.left && t != tp.right)
            return false;
        if (tl != null && (tl.parent != t || tl.hash > t.hash))
            return false;
        if (tr != null && (tr.parent != t || tr.hash < t.hash))
            return false;
        if (t.red && tl != null && tl.red && tr != null && tr.red)
            return false;
        if (tl != null && !checkInvariants(tl))
            return false;
        if (tr != null && !checkInvariants(tr))
            return false;
        return true;
    }

    private static final sun.misc.Unsafe U;
    private static final long LOCKSTATE;

    static {
        try {
            U = sun.misc.Unsafe.getUnsafe();
            Class k = TreeBin.class;
            LOCKSTATE = U.objectFieldOffset(k.getDeclaredField("lockState"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

参考资料:
https://www.sohu.com/a/254192521_355142
https://www.jianshu.com/p/749d1b8db066
https://www.jianshu.com/p/514e33ad6c35
https://www.jianshu.com/p/f93912fec48b
https://blog.csdn.net/sihai12345/article/details/79383766
https://blog.csdn.net/programmeryu/article/details/91606487
红黑树相关知识
https://www.jianshu.com/p/88881fdfcf4c

你可能感兴趣的:(ConcurrentHashMap put源码分析)