ConcurrentHashMap源码解析(1.8版本)

352 阅读30分钟

ConcurrentHashMap(1.8版本)

问题

问题和解答可以作为对ConcurrentHashMap的总体了解。

1. ConcurrentHashMap高效在哪里?

java8版本的ConcurrentHashMap的高效体现在两部分:

  • 数据结构上:使用了哈希表+链表+红黑树,即使在大量的哈希碰撞下也可以快速的检索键值对。
  • 并发上:
    1. 在数组扩容时:支持多线程并发扩容,增强扩容的效率;同时扩容时可以进行读操作,完全不会堵塞,提高读效率;写操作会分成两部分,若写入的对应数组槽位还没有被转化,则可以正常写入,否则该线程会暂停写入,并参与到扩容转化的过程,直到扩容结束后继续进行写操作。进一步细化了写的锁粒度,提高写效率。
    2. 并发写时:只对哈希冲突的键值对造成影响,上锁也只上对应槽位的锁,对数组其他槽位没有影响,降低了锁的粒度。
    3. 在初始化数组和初始化槽位,以及更新锁状态时,大量运用了CAS乐观锁,避免了阻塞、唤醒的开销。

数据结构内部类介绍

  • Node
  • TreeNode
  • TreeBin
  • ForwardingNode

Node

/**
 * Node是用于存放键值的最基本单位
 * Node是单向链式结构
 */
static class Node<K,V> implements Map.Entry<K,V> {
    /**
     * 一般是键的哈希值
     * 也会被用于其他标识,用于其他特殊标识时小于0
     * 若为-1表示当前节点所在的槽位正在初始化或扩容中。
     * 若为-2表示该节点是树状架构。
     */
    final int hash;
    
    /**
     * 键
     */
    final K key;
    
    /**
     * 值
     */
    volatile V val;
    
    /**
     * 下一个节点
     */
    volatile Node<K,V> next;

    Node(int hash, K key, V val, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.val = val;
        this.next = next;
    }

    public final K getKey()       { return key; }
    public final V getValue()     { return val; }
    public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
    
    /**
     * 查找指定键值。
     * 默认是链式查找。
     * 该类子类都会实现对应的find方法来实现各种数据结构的查找节点功能。
     */
    Node<K,V> find(int h, Object k) {
        Node<K,V> e = this;
        if (k != null) {
            do {
                K ek;
                if (e.hash == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
            } while ((e = e.next) != null); // 链式查找
        }
        return null;
    }
    
    /*下面是一些通用的方法,减少子类重复的代码*/
    
    public final String toString(){ return key + "=" + val; }
    public final V setValue(V value) {throw new UnsupportedOperationException();}
    public final boolean equals(Object o) {
        Object k, v, u; Map.Entry<?,?> e;
        return ((o instanceof Map.Entry) &&
                (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
                (v = e.getValue()) != null &&
                (k == key || k.equals(key)) &&
                (v == (u = val) || v.equals(u)));
    }
}

TreeNode

/**
 * TreeNode是红黑树结构的树状节点,用于存储键值对
 * 该节点继承了Node,保留了键值,哈希的属性。
 */
static final class TreeNode<K,V> extends Node<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;

    TreeNode(int hash, K key, V val, Node<K,V> next,
             TreeNode<K,V> parent) {
        super(hash, key, val, next);
        this.parent = parent;
    }

    /**
     * 重写了find方法,用于实现树状结构的查找结点方法
     */
    Node<K,V> find(int h, Object k) {
        return findTreeNode(h, k, null);
    }

    /**
     * 核心查找值得实现方法
     */
    final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
        if (k != null) {
            TreeNode<K,V> p = this;
            do  {
                int ph, dir; K pk; TreeNode<K,V> q;
                TreeNode<K,V> pl = p.left, pr = p.right;
                
                // 比较当前节点哈希和检索的键哈希
                if ((ph = p.hash) > h) 
                    p = pl;
                else if (ph < h)
                    p = pr;
                // 哈希相同,比较键值
                else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                    return p; // 找到了对应的键,返回值
                // 如果哈希相同,键值不同,则通过null值进行排除
                else if (pl == null)
                    p = pr;
                else if (pr == null)
                    p = pl;
                // 无法排除,则尝试通过强制比较进行比对
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    p = (dir < 0) ? pl : pr;
                // 尝试通过右子树查找
                else if ((q = pr.findTreeNode(h, k, kc)) != null)
                    return q;
                else // 排除了所有可能性,只能从左子树查找
                    p = pl;
            } while (p != null);
        }
        return null;
    }
}

TreeBin

/**
 * TreeBin是树状结构的管理节点,是一个标志和管理节点,该节点不存储键值对。
 * 通常遍历槽位第一节点时通过判断该节点是否是TreeBin类来判断该槽位是否是树状结构。
 * TreeBin的hash属性存的值是-2。
 * TreeBin还负责对树的独写上锁控制,红黑树的维护。
 */
static final class TreeBin<K,V> extends Node<K,V> {
    TreeNode<K,V> root; // 树状结构真正的根节点
    
    /**
     * TreeNode继承于Node,因此也包含了链式结构。
     * 树状结构初始化时,先传入链式结构的TreeNode交给TreeBin,
     * TreeBin再负责转化为红黑树。
     * 这里first是链式结构的头节点。
     */
    volatile TreeNode<K,V> first;
    
    /**
     * 被阻塞的线程
     */
    volatile Thread waiter;
    
    /**
     * 锁状态
     * 配合下面标志位使用
     */
    volatile int lockState;
    
    /* 这里是二进制标志位的锁控制 */
    static final int WRITER = 1; // 写锁:0001
    static final int WAITER = 2; // 有线程等待:0010
    // 读锁:0100
    // 读锁是共享锁,因此多个读锁可以兼容。
    // 这里多个读锁lockState只用每次加4
    // 判断是否有人持有读锁只需要把lockstate除以4,看看是否大于0即可。
    static final int READER = 4;

    /**
     * 这个方法在对象a和对象b无法直接比较的情况下
     * 提供一个相对统一的比较标准。
     */
    static int tieBreakOrder(Object a, Object b) {
        int d;
        // 如果a或b是空,
        // 则比较a和b的类名
        // 若a和b类名相同,则使用默认的系统哈希计算方法。
        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;
    }

    /**
     * 传入链式结构的TreeNode
     * 生成树状结构的TreeNode,并由TreeBin管理
     */
    TreeBin(TreeNode<K,V> b) {
        // static final int TREEBIN   = -2;
        super(TREEBIN, null, null, null);
        this.first = b;
        TreeNode<K,V> r = null;
        for (TreeNode<K,V> x = b, next; x != null; x = next) {
            next = (TreeNode<K,V>)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<K,V> p = r;;) {
                    int dir, ph;
                    K pk = p.key;
                    // 通过哈希比较
                    if ((ph = p.hash) > h)
                        dir = -1;
                    else if (ph < h)
                        dir = 1;
                    // 通过comparable比较
                    else if ((kc == null &&
                              (kc = comparableClassFor(k)) == null) ||
                             (dir = compareComparables(kc, k, pk)) == 0)
                        // 强制比较
                        dir = tieBreakOrder(k, pk);
                        TreeNode<K,V> 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;
                    }
                }
            }
        }
        this.root = r;
        assert checkInvariants(root);
    }

    /**
     * 对树上锁
     */
    private final void lockRoot() {
        // 用CAS上锁
        if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER))
            contendedLock(); // 竞争上锁
    }

    /**
     * 解锁
     */
    private final void unlockRoot() {
        lockState = 0;
    }

    /**
     * 竞争上锁
     */
    private final void contendedLock() {
        boolean waiting = false;
        for (int s;;) {
            // ((s = lockState) & ~WAITER) == 0 表示读锁和写锁都没锁
            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);
            
            // 若所有if都没踩中,表示当前这棵树有竞争,且不止一个,
            // 本线程会一直自旋直至能够设为等待线程或者获取到锁。
        }
    }

    /**
     * 重写了Node的find方法
     * 主要是控制树状结构查找时的锁状态
     */
    final Node<K,V> find(int h, Object k) {
        if (k != null) {
            for (Node<K,V> e = first; e != null; ) {
                int s; K ek;
                // 如果上了写锁,则进行链式查找
                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<K,V> r, p;
                    try {
                        // 树状搜索调用树状节点的find方法。
                        // 树状搜索都是从树的头节点开始搜索。
                        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;
    }

    /**
     * 移除指定节点
     * @return 如果树很小则返回true,反则返回false
     */
    final boolean removeTreeNode(TreeNode<K,V> p) {
        
        TreeNode<K,V> next = (TreeNode<K,V>)p.next;
        TreeNode<K,V> pred = p.prev;  
        TreeNode<K,V> r, rl;
        // 表示要去除的节点如果是头节点
        if (pred == null) 
            first = next;  // 修改头节点
        else
            pred.next = next; // 从节点链表中剔除
        if (next != null)
            next.prev = pred; 
        // 若first为空,表示整棵树为空树
        if (first == null) {
            root = null;
            return true;
        }
        if ((r = root) == null || r.right == null ||
            (rl = r.left) == null || rl.left == null)
            // 来到这里表示这棵树太小
            return true;
        lockRoot(); //对整棵树上锁
        try {
            TreeNode<K,V> replacement;
            TreeNode<K,V> pl = p.left;
            TreeNode<K,V> pr = p.right;
            if (pl != null && pr != null) {
                TreeNode<K,V> s = pr, sl;
                // 寻找被替换节点的右子树的最左非空节点
                while ((sl = s.left) != null)
                    s = sl;
                // 下面是在红黑树中删除节点 
                boolean c = s.red; s.red = p.red; p.red = c;
                TreeNode<K,V> sr = s.right;
                TreeNode<K,V> pp = p.parent;
                if (s == pr) { // 如果s是p的直接右节点
                    p.parent = s;
                    s.right = p;
                }
                else {
                    TreeNode<K,V> sp = s.parent;
                    if ((p.parent = sp) != null) {
                        if (s == sp.left)
                            sp.left = p;
                        else
                            sp.right = p;
                    }
                    if ((s.right = pr) != null)
                        pr.parent = s;
                }
                p.left = null;
                if ((p.right = sr) != null)
                    sr.parent = p;
                if ((s.left = pl) != null)
                    pl.parent = s;
                if ((s.parent = pp) == null)
                    r = s;
                else if (p == pp.left)
                    pp.left = s;
                else
                    pp.right = s;
                if (sr != null)
                    replacement = sr;
                else
                    replacement = p;
            }
            else if (pl != null)
                replacement = pl;
            else if (pr != null)
                replacement = pr;
            else
                replacement = p;
            if (replacement != p) {
                TreeNode<K,V> pp = replacement.parent = p.parent;
                if (pp == null)
                    r = replacement;
                else if (p == pp.left)
                    pp.left = replacement;
                else
                    pp.right = replacement;
                p.left = p.right = p.parent = null;
            }

            root = (p.red) ? r : balanceDeletion(r, replacement);

            if (p == replacement) {  // detach pointers
                TreeNode<K,V> 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;
    }

    /* ------------------------------------------------------------ */
    // Red-black tree methods, all adapted from CLR

    /**
     * 这个方法用于维护红黑树的性质
     */
    static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                          TreeNode<K,V> p) {
        // 此处忽略维护红黑树的代码逻辑
    }

    /**
     * 这个方法用于维护红黑树的性质
     */
    static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                           TreeNode<K,V> p) {
        // 此处忽略维护红黑树的代码逻辑
    }

    /**
     * 这个方法用于增加节点后维护红黑树的性质
     */
    static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                TreeNode<K,V> x) {
        // 此处忽略维护红黑树的代码逻辑
    }

    /**
     * 这个方法用于删除节点后维护红黑树的性质
     */
    static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                               TreeNode<K,V> x) {
        // 此处忽略维护红黑树的代码逻辑
    }

    /**
     * 检查链式结构和树状结构的准确性
     */
    static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
        TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
            tb = t.prev, tn = (TreeNode<K,V>)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);
        }
    }
}

ForwardingNode

/**
 * 这是一个特殊的控制节点
 * 当数组正在扩容时,旧数组已移植的槽位会被换上该类节点
 * 该类节点保存了新的数组,旧数组可以通过该节点访问到新数组的数据。
 * 一、 在读取值时存在两种情况:
 * 1. 若数组转化过程中还未转化到或正在转化指定槽位:
 *   访问旧数组该槽位时,要么是链式节点要么是树状节点,可以照常访问;
 * 2. 若数组转化已转化完指定槽位:
 *   访问旧数组指定槽位时会发现该槽位被换成ForwardingNode节点,可以通过该节点去到新数组并访问新数组的值。
 *
 * 二、 在写值时存在两种情况:
 * 1. 第一种情况和读类似,因为没有感知到节点正在扩容,因此可以正常插入新值。
 * 2. 第二种情况,发现正在扩容时,则调用helpTransfer方法,参与共同扩容。扩容后新数组已变成默认数组,可以正常读取。
 */
static final class ForwardingNode<K,V> extends Node<K,V> {
    final Node<K,V>[] nextTable;
    ForwardingNode(Node<K,V>[] tab) {
        super(MOVED, null, null, null);
        this.nextTable = tab;
    }

    /**
     * 重写了Node的find方法
     * 可以间接从旧数组访问到新数组一转化好的槽位。
     */
    Node<K,V> find(int h, Object k) {
        // 在新的数组查找
        outer:  for (Node<K,V>[] tab = nextTable;;) {
            Node<K,V> e; int n;
            if (k == null || tab == null || (n = tab.length) == 0 ||
                (e = tabAt(tab, (n - 1) & h)) == null)
                return null;
            for (;;) {
                int eh; K ek;
                if ((eh = e.hash) == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
                    
                // 节点非链式结构
                if (eh < 0) {
                    // 节点是ForwardingNode类型,说明又一次发生了扩容
                    if (e instanceof ForwardingNode) {
                        // 获得最新的ForwardingNode节点。
                        tab = ((ForwardingNode<K,V>)e).nextTable;
                        continue outer;
                    }
                    else
                        return e.find(h, k); // 调用对应的查找方式
                }
                // 链式查找
                if ((e = e.next) == null)
                    return null;
            }
        }
    }
}

存取机制

put方法

概述

  • put方法用于存放键值,该方法并发安全。

代码

public V put(K key, V value) {
    // 核心代码是调用了putVal方法。
    return putVal(key, value, false);
}
putVal
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 不允许存储null值
    // ConcurrentHashMap的null值默认表示取不到值。
    if (key == null || value == null) throw new NullPointerException();
    
    // 在key的hash基础上进一步对哈希进行处理,
    // 使哈希更加均匀的散步到table的槽位上。
    int hash = spread(key.hashCode());
    
    int binCount = 0; // 用于控制addCount方法的操作位
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        
        // 如果table还是空值,则先初始化。
        if (tab == null || (n = tab.length) == 0)
            tab = initTable(); // 初始化代码放在后面。
            
        // (n - 1) & hash 可以把hash映射到索引上
        // 这里把获取槽位和判断语句合在一起,可以精简代码。
        // 这个分支判断该槽位是否已经放有节点。
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            // 如果该槽位为空,则通过cas插入该键值节点。
            // 节点构造函数的参数依次是:哈希、键、值、下一个节点
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   
        }
        // 哈希是MOVED是一个特殊的节点ForwardingNode
        // 该节点表示当前这个槽位的节点正在扩容转化中。
        // 这个分支是判断该槽位是否处于转化状态。
        else if ((fh = f.hash) == MOVED)
            // 该线程参与到转化过程中。
            // ConcurrentHashMap支持并发转化。
            tab = helpTransfer(tab, f); 
        // 排除上面可能,则开始放值。
        else {
            V oldVal = null; // 如果该键已存在,这个变量用于放原来的值。
            synchronized (f) {  // 锁当前槽位
                if (tabAt(tab, i) == f) { // 双重检验
                    // 哈希大于零则是链表结构
                    if (fh >= 0) {
                        binCount = 1;
                        // 遍历链表
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            // 如果找到对应的键,则替代旧值
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            // 找不到则生成新节点插入链尾
                            Node<K,V> pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key,
                                                          value, null);
                                break;
                            }
                        }
                    }
                    // 判断是否是树状结构。
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        // 通过内置的TreeBin类方法插入新值。
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            if (binCount != 0) {
                // 如果节点数量大于树化阈值,
                // 则对该槽位进行树化操作。
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                // oldVal为空表示该键本身已存在,
                // 这里只替换了旧值,并没有插入新值。
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    // 重新统计数量。
    addCount(1L, binCount);
    return null;
}
initTable方法
// 用于初始化table
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        // sizeCtl小于0表示table正在初始化
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // 本线程暂时退让,等待table初始化完毕
        // 通过CAS把sizeCtl替换成-1
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    // sizeCtl大于0时用于记录table的大小。
                    // 若sizeCtl没有被初始化,则使用默认值16.
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);
                }
            } finally {
                // 最后sizeCtl保存需要扩容数组的元素数量临界值。
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

get方法

// 取值入口
public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
    int h = spread(key.hashCode()); // 计算哈希
    
    // 定位槽位,取出槽位头节点
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) {
        // 比较头节点哈希
        if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                return e.val;
        }
        // 如果头节点是树状结构或者是转化中状态
        else if (eh < 0)
            // 调用各自类型节点实现的find方法。 
            return (p = e.find(h, key)) != null ? p.val : null;
        
        // 顺序遍历
        while ((e = e.next) != null) {
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}

数组扩容机制

控制属性

/**
  * 数组初始化和重组控制符。
  * 负数时:数组在初始化时为-1,若是在重组,则该值的相反数减1是参与转化的线程数。
  * 正数时:该值表示数组需要被重组时的元素数量。
  * 等于0:数组为空或还没被初始化。
  */
private transient volatile int sizeCtl;

private static final long SIZECTL; // sizeCtl的指针。

/**
 * 最小步幅
 * 表示并发转化下,每次分配给线程的最少的槽位数。
 */
private static final int MIN_TRANSFER_STRIDE = 16; // 最小步幅

/**
 * 用于生成重组标记。
 */
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;

Transfer方法

概述

  • transfer方法是对当前数组进行扩容,通常扩容到当前大小的两倍,且扩容前后大小都是2的幂次方。
  • tranfser方法支持多线程,因此有个配套的helpTransfer方法。sizeCtl属性的也会记录参与transfer的线程数量。
  • 特殊的头节点ForwardingNode:该节点头的hash是-1,表示当前槽位正在转化过程中。该特殊节点包含了新的数组,所有的旧数组槽位若被转化过,都会被替换成这个特殊节点,从而不管访问哪个就节点都能快速找到到新的数组,并以特殊的针对转化中方法去寻找对应的键值对。
  • transfer方法有个概念叫“步幅”,步幅定义了一个线程一次可以分配多少个数组槽位给当前线程转化。步幅最小是16。几个属性和变量:
    • stride:步幅,通过当前电脑CPU所拥有的核心数,以及当前数组大小进行计算,得出一个合理的步幅值。
    • TRANSFERINDEX、transferIndex:transferIndex是全局变量,用来表示当前数组槽位以分派到哪一个槽位。分派方式是从高索引往低索引分派。TRANSFERINDEX是指向transferIndex的指针,是为了更好的提高该属性的线程可见度,及时感知该值是否被修改。
    • bound:界限,bound定义了当前线程所转化的槽位的界限,若转化的索引到达了bound,则需要重新申请新的一组转化槽位。

过程

  1. transfer方法会申请是当前数组两倍大小的新数组。
  2. 生成特殊节点ForwardingNode。
  3. 申请当前线程转化的数组槽位。
  4. 遍历需要转化的槽位,遇到几种情况:
    1. 该槽位是null,给他赋上ForwardingNode。
    2. 该槽位已经是ForwardingNode,那么表示该槽位已经被转化过了或者已经有线程正在转化它,当前线程直接跳过。
    3. 不是上面两种情况,则当前线程对该槽位的头节点上锁,并遍历当前槽位的节点并转化到新数组中,把当前槽位的节点替换成ForwardingNode。
  5. 若当前线程转化完当前的所有数组槽位,则重新申请新的数组槽位进行转化。
  6. 转化完成后,把新数组设为默认数组,设置新的阈值:新数组大小的0.75倍。
  7. 转化结束。

代码


// 转化时用于分割不同线程下的数组槽位。
// 当线程申请转化的数组槽位时,transferIndex就会向左移动步幅的大小,切割出一部分槽位交给该线程转化
private transient volatile int transferIndex; 

private static final long TRANSFERINDEX; // 它是transferIndex的指针,可以看成就是transferIndex本身。

private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    int n = tab.length, stride;
    // 下面if用于计算步幅
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE; 
    
    // 初始化新的数组
    if (nextTab == null) { 
        try {
            @SuppressWarnings("unchecked")
            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
            nextTab = nt;
        } catch (Throwable ex) {      // try to cope with OOME
            sizeCtl = Integer.MAX_VALUE;
            return;
        }
        nextTable = nextTab;
        transferIndex = n;
    }
    int nextn = nextTab.length;
    
    // 特殊的节点,保存有新的数组,用于标记数组槽位正在转化操作。
    ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
    
    boolean advance = true; // 用于控制线程访问数组槽位的标记
    boolean finishing = false;  // 用于标记转化是否完成
    // i是用于遍历数组的槽位,一般从高索引向低索引遍历。
    // i不是连续顺序遍历,是在步幅范围内顺序,但步幅与步幅间可能存在跳跃。
    // bond定义了当前的线程转化的槽位的界限。
    for (int i = 0, bound = 0;;) {
        Node<K,V> f; int fh;
        while (advance) {
            int nextIndex, nextBound;
            // i在bound界限之内,顺序执行。
            if (--i >= bound || finishing)
                advance = false;
            // i不在界限之内,则判断transferIndex是否已经分割完槽位,若分割完则停止获取转化槽位。
            else if ((nextIndex = transferIndex) <= 0) {
                i = -1;
                advance = false;
            }
            // 若还有未分割的槽位
            // 则通过CAS尝试获取用于本线程转化的槽位。
            else if (U.compareAndSwapInt
                     (this, TRANSFERINDEX, nextIndex,
                      nextBound = (nextIndex > stride ?
                                   nextIndex - stride : 0))) {
                bound = nextBound;
                i = nextIndex - 1;
                advance = false;
            }
        }
        
        // 先判断完成转化的标志位
        if (i < 0 || i >= n || i + n >= nextn) {
            int sc;
            if (finishing) {
                nextTable = null;
                table = nextTab; // 新的数组设为默认数组
                sizeCtl = (n << 1) - (n >>> 1); // 设置新的阈值,0.75倍新数组大小
                return;
            }
            // 设置变量参数sizeCtl
            if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                // 若满足下面的条件,则表示当前线程是最后一个转化线程,其他转化线程已全部结束。
                if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                    return;
                finishing = advance = true;
                i = n;
            }
        }
        // 当前槽位还没有初始化,用CAS初始化成ForwardingNode节点。
        else if ((f = tabAt(tab, i)) == null)
            advance = casTabAt(tab, i, null, fwd);
        // 哈希值是MOVED则是ForwardingNode节点。
        // 若当前节点已经是ForwardingNode节点,则表示该槽位已被转化,跳过。
        else if ((fh = f.hash) == MOVED)
            advance = true;
        // 排除了其他可能性,开始转化当前槽位。
        else {
            synchronized (f) { // 上锁
                // 双重校验
                // 当前线程获取到锁时,其他线程已经把这个槽位给转化了。
                // 若被转化过,该节点会变成ForwardingNode。
                if (tabAt(tab, i) == f) {
                    Node<K,V> ln, hn;
                    // 判断是不是链表节点
                    if (fh >= 0) {
                        // 由于ConcurrentHashMap的特殊映射机制,
                        // 若分配到原索引槽位,runbit则为0,
                        // 若分配到新索引槽位(该槽位只可能是旧索引+就数组大小),则为1。
                        int runBit = fh & n; 
                        Node<K,V> lastRun = f;
                        // 这个for循环会取出最后一段节点,这段节点都是放置在同一个槽位的,
                        // 可能是新索引槽位,也可能是旧索引槽位,这一段节点可能也只有一个节点。
                        for (Node<K,V> 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;
                        }
                        // 从头遍历节点,插入到对应的槽位中。
                        for (Node<K,V> 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<K,V>(ph, pk, pv, ln);
                            else
                                hn = new Node<K,V>(ph, pk, pv, hn);
                        }
                        
                        // 更新两个对应的索引槽位
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        // 就数组槽位设为ForwardingNode节点。
                        // 当转换为ForwardingNode节点表示该槽位已经转换完毕。
                        setTabAt(tab, i, fwd);
                        // 该槽位处理完毕,重新获取新的待转化槽位。
                        advance = true;
                    }
                    // 若该槽位的节点是树状结构
                    else if (f instanceof TreeBin) {
                        TreeBin<K,V> t = (TreeBin<K,V>)f; // 旧数组槽位中的树
                        // ConcurrentHashMap的树状结构节点也保留了列表的结构。
                        // 一开始初始化先是转化成列表结构
                        // 在根据节点数量判断是否要转化成树状结构。
                        TreeNode<K,V> lo = null, loTail = null; // 新数组旧索引槽位的树节点,及对应的树尾巴
                        TreeNode<K,V> hi = null, hiTail = null; // 新数组新索引槽位的树节点,及对应的树尾巴
                        int lc = 0, hc = 0;
                        for (Node<K,V> e = t.first; e != null; e = e.next) {
                            int h = e.hash;
                            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
                                    loTail.next = p;
                                loTail = p;
                                ++lc;
                            }
                            // 新槽位
                            else {
                                if ((p.prev = hiTail) == null)
                                    hi = p;
                                else
                                    hiTail.next = p;
                                hiTail = p;
                                ++hc;
                            }
                        }
                        // 判断节点数是否需要进行树化操作还是退化成节点结构
                        ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                            (hc != 0) ? new TreeBin<K,V>(lo) : t;
                        hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                            (lc != 0) ? new TreeBin<K,V>(hi) : t;
                            
                        // 跟上面节点结构的结束类似。
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd);
                        advance = true;
                    }
                }
            }
        }
    }
}

helpTransfer方法

/**
 * 该方法可以使当前线程参与到transfer中。
 * @param tab 数组
 * @param f 槽位头节点
 */
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
    Node<K,V>[] nextTab; int sc;
    // 数组不为空,且槽位中的头节点是ForwardingNode节点
    if (tab != null && (f instanceof ForwardingNode) &&
        (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
        int rs = resizeStamp(tab.length);  // 重组标记
        while (nextTab == nextTable && table == tab &&
               (sc = sizeCtl) < 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;
}

tryPresize方法

/**
 * 尝试扩容数组
 */
private final void tryPresize(int size) {
    // 限制最大容量
    int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
        tableSizeFor(size + (size >>> 1) + 1);
    int sc;
    while ((sc = sizeCtl) >= 0) {
        Node<K,V>[] tab = table; int n;
        // 数组还没有被初始化,先初始化
        if (tab == null || (n = tab.length) == 0) {
            n = (sc > c) ? sc : c; // sc和c取大者作为容量
            
            // 把sizeCtl设为-1状态,标志数组正在重组
            if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if (table == tab) {
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
            }
        }
        // c还小于重组阈值或者当前大小已经达到最大的数组大小,
        // 则不扩容数组。
        else if (c <= sc || n >= MAXIMUM_CAPACITY)
            break;
        else if (tab == table) {
            int rs = resizeStamp(n); // 获取重组标记
            // 若数组正在扩容
            if (sc < 0) {
                Node<K,V>[] nt;
                // 判断是否还在重组
                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))
                transfer(tab, null);
        }
    }
}

树化和逆树化

相关属性

/**
 * 树化阈值
 * 当一个槽位的节点数大于该阈值时,
 * 则对该槽位的链式节点转化为树状结构
 */
static final int TREEIFY_THRESHOLD = 8;

/**
  * 逆树化阈值
 * 当一个槽位的节点数小于于该阈值时,若该结构是树状结构,
 * 则转化为链式结构。
 */
static final int UNTREEIFY_THRESHOLD = 6;

/**
 * 最小树化阈值
 * 当槽位节点达到树化标准而数组大小没有达到该阈值时
 * 则通过扩容数组来避免树化操作。
 */
static final int MIN_TREEIFY_CAPACITY = 64; 

treeifyBin方法

/**
 * 用于把链式节点转化成树状节点
 * tab 对应的数组
 * index 要转化的索引槽位
 */
private final void treeifyBin(Node<K,V>[] tab, int index) {
    Node<K,V> b; int n, sc;
    if (tab != null) {
        // 如果没有达到树化最小阈值 
        if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
            tryPresize(n << 1); // 扩容数组代替树化操作。
        else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
            synchronized (b) { // 上锁
                if (tabAt(tab, index) == b) { // 双重校验
                    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
                            tl.next = p;
                        tl = p;
                    }
                    // treeBin是树状结构的头节点
                    // 把树状节点的树状结构搭建起来的操作在TreeBin的构造器里进行。
                    setTabAt(tab, index, new TreeBin<K,V>(hd));
                }
            }
        }
    }
}

/**
 * 这是TreeBin的构造器
 * 包含了把链式节点转化为红黑树的过程
 */
TreeBin(TreeNode<K,V> b) {
    super(TREEBIN, null, null, null);
    this.first = b;
    TreeNode<K,V> r = null;
    // 遍历链式结构的树状节点
    for (TreeNode<K,V> x = b, next; x != null; x = next) {
        next = (TreeNode<K,V>)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<K,V> 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<K,V> 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;
                }
            }
        }
    }
    this.root = r;  // TreeBin类会持有根节点
    assert checkInvariants(root); // 检查树状结构是否正确。
}

/**
 * 检查t的树状结构是否正确。
 */
static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
    TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
        tb = t.prev, tn = (TreeNode<K,V>)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;
}

untreeify

/**
 * 逆树化操作
 */
static <K,V> Node<K,V> untreeify(Node<K,V> b) {
    Node<K,V> hd = null, tl = null;
    for (Node<K,V> q = b; q != null; q = q.next) {
        Node<K,V> p = new Node<K,V>(q.hash, q.key, q.val, null);
        if (tl == null)
            hd = p;
        else
            tl.next = p;
        tl = p;
    }
    return hd;
}

计数机制

  • ConcurrentHashMap的计数机制由一个基础值加上一个数组里面的所有值得到总和。

相关属性和类

/**
 * 基础计数值
 * 若没有发生竞争情况,则使用这个值计数。
 */
private transient volatile long baseCount;

/**
 * baseCount的指针
 */
private static final long BASECOUNT;

/**
 * 用于并发情况下的计数单位
 */
@sun.misc.Contended static final class CounterCell {
    volatile long value;
    CounterCell(long x) { value = x; }
}

/**
 * 用于并发计数数组
 * 增加时,线程随机映射到该数组的一个槽位,并把里面的值加上增加的数值。
 */
private transient volatile CounterCell[] counterCells;

/**
 * 自旋锁,用于锁CounterCell数组
 */
private transient volatile CounterCell[] counterCells;

private static final long CELLSBUSY; // counterCells的指针

addCount方法

/** 
 * 添加ConcurrentHashMap记录的元素数量
 * x 增加的数量
 * check 控制数
 * 1.小于0时不检查是否需要扩容;
 * 2.小于等于1时,若不发生竞争则检查;
 * 3.非以上情况则检查。
 */
private final void addCount(long x, int check) {
    CounterCell[] as; long b, s;
    // 如果CounerCell还没有初始化且修改baseCount没有遇到竞争,
    // 则直接把新增数量加到baseCount中。
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        // 来到此处表示CounerCell初始化过或者存在多个线程竞争添加数量
        CounterCell a; long v; int m;
        boolean uncontended = true;
        // 如果CounerCell还没初始化,
        // 或者随机获取一个CounerCell槽位,该槽位还没初始化,
        // 或者该槽位已经初始化了,但修改该槽位的值时发生了竞争
        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;
        }
        // 运行到这表示本次增加次数没有遇到竞争
        if (check <= 1)
            return;
        s = sumCount(); // 计算总和
    }
    // 扩容数组
    if (check >= 0) {
        Node<K,V>[] tab, nt; int n, sc;
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
               (n = tab.length) < MAXIMUM_CAPACITY) {
            int rs = resizeStamp(n);
            // 若sc小于0表示当前数组已经在重组中
            if (sc < 0) {
                // 查看重组标志是否一致,若不一致表示没有在重组或本轮重组已结束
                // transferIndex <= 0 表示重组是否已经结束
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0)
                    break;
                // 协助重组的线程数加1,
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            }
            // 这里表示数组没有被重组,当前线程开始对数组进行重组
            else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                         (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null);
            s = sumCount(); // 求出ConcurrentHashMap的元素数量
        }
    }
}

sumCount方法

/** 
 * 计算ConcurrentHashMap存储的元素数量
 * 通过遍历CounterCell数组,把值累加,最后加上baseCount即得到结果。
 */
final long sumCount() {
    CounterCell[] as = counterCells; CounterCell a;
    long sum = baseCount;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
}

fullAddCount方法

/**
 * 添加值
 * 该方法用于竞争十分激烈的时候
 * 或者用于初始化CounterCell数组或指定槽位。
 */
private final void fullAddCount(long x, boolean wasUncontended) {
    // 这里初始化ThreadLocalRandom类。
    // 该类用于当前线程获取随机数。
    // 并获取随机值h
    int h;
    if ((h = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();      
        h = ThreadLocalRandom.getProbe();
        wasUncontended = true;
    }
    
    boolean collide = false; // 表示要放入的槽位是否发生碰撞
    for (;;) {
        CounterCell[] as; CounterCell a; int n; long v;
        // 如果数组不为空
        if ((as = counterCells) != null && (n = as.length) > 0) {
            // 把随机数h映射到一个槽位,即获取一个随机槽位
            // 若该槽位为空,则初始化该槽位
            if ((a = as[(n - 1) & h]) == null) {
                // 这里判断有没有上锁,如果上锁cellBusy是1
                if (cellsBusy == 0) {   
                    CounterCell r = new CounterCell(x); // x是新增的数量
                    // 尝试上锁cellsBusy
                    if (cellsBusy == 0 &&
                        U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                        boolean created = false;
                        try { 
                            CounterCell[] rs; int m, j;
                            // 再次确认该槽位没有被初始化,否则可能把已初始化的值覆盖掉。
                            if ((rs = counterCells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            cellsBusy = 0;
                        }
                        if (created)
                            break; // 初始化成功,退出循环
                        // 执行到这表示有多个线程执行了个这个分支。
                        // 且其他线程抢先初始化了这个槽位,
                        // 导致当前线程初始化这个槽位失败。
                        continue; 
                    }
                }
                collide = false;
            }
            // 来到这则认为存在竞争
            else if (!wasUncontended)
                wasUncontended = true;
            // 尝试通过cas更新猜槽位的值
            else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                break;
            // counterCells != as 表示数组被重组了。
            else if (counterCells != as || n >= NCPU)
                collide = false;
            // 来到这里表示肯定发生了碰撞
            else if (!collide)
                collide = true;
            // 这里尝试对数组上锁
            // 若上锁成功将进行扩容操作
            else if (cellsBusy == 0 &&
                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                try {
                    if (counterCells == as) {
                        // 扩容到原来的两倍 
                        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;
            }
            // 获取新的随机码
            h = ThreadLocalRandom.advanceProbe(h);
        }
        // 来到这表示数组还没被初始化
        // 通过获取锁尽行初始化操作
        else if (cellsBusy == 0 && counterCells == as &&
                 U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
            boolean init = false;
            try {                           // Initialize table
                if (counterCells == as) {
                    CounterCell[] rs = new CounterCell[2];
                    rs[h & 1] = new CounterCell(x);
                    counterCells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            if (init)
                break;
        }
        // 来到这表示获取锁失败,直接尝试增加到baseCount。
        else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
            break;                          // Fall back on using base
    }
}

size方法

public int size() {
    long n = sumCount(); // 获取计数值
    return ((n < 0L) ? 0 :
            (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
            (int)n); // 转化成int类型
}