【java集合】ConcurrentHashMap源码分析

重要常量

/* ---------------- Constants -------------- */

    /**
     * table最大容量是2的30次方
     */
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * table默认初始化容量16  扩容总是2的n次方。
     */
    private static final int DEFAULT_CAPACITY = 16;

    /**
     * The largest possible (non-power of two) array size.
     * Needed by toArray and related methods.
     */
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * The default concurrency level for this table. Unused but
     * defined for compatibility with previous versions of this class.
     */
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    /**
     * 负载因子
     */
    private static final float LOAD_FACTOR = 0.75f;

    /**
     * 树化阈值,当桶内链表长度>=8时,会将链表转成红黑树 
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * 当桶内node小于6时,红黑树会转成链表。
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * table的总容量要大于64,桶内链表才转换为树形结构,否则当桶内链表长度>=8 时会优先扩容。 
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * Minimum number of rebinnings per transfer step. Ranges are
     * subdivided to allow multiple resizer threads.  This value
     * serves as a lower bound to avoid resizers encountering
     * excessive memory contention.  The value should be at least
     * DEFAULT_CAPACITY.
     */
    private static final int MIN_TRANSFER_STRIDE = 16;

    /**
     * The number of bits used for generation stamp in sizeCtl.
     * Must be at least 6 for 32bit arrays.
     */
    private static int RESIZE_STAMP_BITS = 16;

    /**
     * The maximum number of threads that can help resize.
     * Must fit in 32 - RESIZE_STAMP_BITS bits.
     */
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

    /**
     * The bit shift for recording size stamp in sizeCtl.
     */
    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;



	/*
     * Encodings for Node hash fields. See above for explanation.
     */
    static final int MOVED     = -1; // hash for forwarding nodes
    static final int TREEBIN   = -2; // hash for roots of trees
    static final int RESERVED  = -3; // hash for transient reservations
    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash

	/** Number of CPUS, to place bounds on some sizings */
    static final int NCPU = Runtime.getRuntime().availableProcessors();

	/**
     * 整个hash表的结构。也被称作hash桶数组 
     */
    transient volatile Node<K,V>[] table;

    /**
     * The next table to use; non-null only while resizing.
     */
    private transient volatile Node<K,V>[] nextTable;

    /**
     * Base counter value, used mainly when there is no contention,
     * but also as a fallback during table initialization
     * races. Updated via CAS.
     */
    private transient volatile long baseCount;

    /**
     * 该字段控制table的初始化和扩容。
	 * sizeCtl<0 时,表示table初始化或者扩容。
	 * 		sizeCtl = -1 表示已经初始化。
	 * 		sizeCtl = -(1+正在扩容的线程数)
	 * 大于零,表示size * 0.75。
     */
    private transient volatile int sizeCtl;

    /**
     * The next table index (plus one) to split while resizing.
     */
    private transient volatile int transferIndex;

    /**
     * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
     */
    private transient volatile int cellsBusy;

    /**
     * Table of counter cells. When non-null, size is a power of 2.
     */
    private transient volatile CounterCell[] counterCells;

    // views
    private transient KeySetView<K,V> keySet;
    private transient ValuesView<K,V> values;
    private transient EntrySetView<K,V> entrySet;

内部类

在这里插入图片描述

Node

static class Node<K,V> implements Map.Entry<K,V> {
		/**
		 * hash值
		 * -1为ForwardingNode表示正在扩容
		 * -2为TreeBin表示桶内为红黑树
		 * 大于0,表示桶内为链表
		 */
        final int hash;
        final K key;
        volatile V val;
        // 下一个Node
        volatile Node<K,V> next;
        ...
}

TreeNode 继承自Node,含有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;
        ...
}

TreeBin 树的包装类Node

static final class TreeBin<K,V> extends Node<K,V> {
		// 红黑色的根节点
        TreeNode<K,V> root;
        // 链表头节点
        volatile TreeNode<K,V> first;
        //
        volatile Thread waiter;
        //
        volatile int lockState;
        // values for lockState 锁状态标识
        // 1 写锁
        static final int WRITER = 1; // set while holding write lock
        // 2 等待写锁
        static final int WAITER = 2; // set when waiting for write lock
        // 4 读锁
        static final int READER = 4; // increment value for setting read lock
}

ForwardingNode

static final class ForwardingNode<K,V> extends Node<K,V> {
        final Node<K,V>[] nextTable;
}

在扩容时使用,如果旧table的一个桶中全部的节点都迁移到新table中,旧table就在这个桶中放置一个ForwardingNode。
读操作或者迭代读时碰到ForwardingNode时,将操作转发到扩容后的新的table上去执行。
写操作碰见它时,则尝试帮助扩容。

Unsafe类方法

@SuppressWarnings("unchecked")  
// transient volatile Node<K,V>[] table; table成员变量定义用volatile修饰 
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
	// 如果tab是volatile变量,则该方法保证其可见性
	return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}

// 通过CAS设置table索引为 i 处的元素
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) {
	return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
// 修改table 索引 i 处的元素
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
	// 如果tab是volatile变量,则该方法保证其可见性
	U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}

构造函数

  1. 无参构造函数
	/**
     * 啥也不做,使用默认容量16
     */
    public ConcurrentHashMap() {
    }
  1. 传入初始容量的构造函数
    并不是把入参当作容量,table的容量必须是2的N次方
	public ConcurrentHashMap(int initialCapacity) {
		// 入参<0,抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
        // 给定容量大于最大容量,则使用最大容量 2的30次方,否则需要计算
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   // tableSizeFor和HashMap对比传参是不一样的
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;
    }

对比一下HashMap和ConcurrentHashMap的tableSizeFor()方法传参

// tableSizeFor(initialCapacity);
// HashMap初始化容量时,转化为大于等于设置值的2的幂次数
Map map = new HashMap(2)    实际初始化的大小为2
Map map = new HashMap(14)   实际初始化的大小为16
Map map = new HashMap(16)   实际初始化的大小为16

// tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
// ConcurrentHashMap初始化设置容量时,底层会自动转换为距设置值2倍的最近的2的次幂;
// 前后距离相等时选择向后转换:如3 * 2 = 6距离4和8相等,选择向后转换为8
Map map = new ConcurrentHashMap(2)    实际初始化的大小为4
Map map = new ConcurrentHashMap(14)   实际初始化的大小为32
Map map = new ConcurrentHashMap(16)   实际初始化的大小为32
  1. 带负载因子的构造函数
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
        this(initialCapacity, loadFactor, 1);
    }
  1. 带concurrencyLevel的构造函数
    concurrencyLevel只是为了兼容1.7,并不是实际的并发等级
	public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
            initialCapacity = concurrencyLevel;   // as estimated threads
        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
        this.sizeCtl = cap;
    }

table的初始化

和HashMap一样都是在首次put过程中判断出table数组为空或者length为0时才调用进行初始化的

	/**
     * Initializes table, using the size recorded in sizeCtl.
     * 使用sizeCtl中记录的大小初始化table。
     */
    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
        	/**
        	 * -1  其他线程正在初始化  当前线程yield
        	 * -(1+n) 其他n个线程正在扩容  当前线程yield
        	 *  0 使用了无参构造器,sizeCtl为默认值0  其他线程yield
        	 *  大于0 已经在构造器中为sizeCtl重新赋值  其他线程yield
        	 */
            if ((sc = sizeCtl) < 0)
            	// 让出CPU资源
                Thread.yield(); // lost initialization race; just spin
            //修改 sizeCtl 的值为 -1  SIZECTL 为 sizeCtl 的内存地址(通过Unsafe直接操作内存)
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                    	// 用户有指定初始化容量,就用用户指定的,否则用默认的16
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        // 计算扩容阈值,比如: 16-4 = 12
                        sc = n - (n >>> 2);
                    }
                } finally {
                	// 设置阈值
                	// ps: 本来sizeCtl就是存放扩容阈值的,table初始化前没用到,所以暂时用来存用户指定容量时计算出来的容量,HashMap的threShold也是这样用的
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

put流程

public V putIfAbsent(K key, V value) {
		// true表示key已经存在时,put失败
        return putVal(key, value, true);
    }
public V put(K key, V value) {
		// false 表示key已经存在时,替换value
        return putVal(key, value, false);
    }

    final V putVal(K key, V value, boolean onlyIfAbsent) {
    	// key和value都不能为null,而HashMap是可以的
        if (key == null || value == null) throw new NullPointerException();
        // 计算Hash值,就一行代码return (h ^ (h >>> 16)) & HASH_BITS; // static final int HASH_BITS = 0x7fffffff // 0x7fffffff => 0b 01111111111111111111111111111111
        // 保证hash码最高位为0,即正数
        // Node节点的hash值(-1为ForwardingNode表示正在扩容,-2为TreeBin表示桶内为红黑树,大于0表示桶内为链表。)
        int hash = spread(key.hashCode());
        // 用来计算在这个桶总共有多少个元素,用来控制扩容或者转移为树
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) { // 迭代桶数组,自旋
        	// f: 桶头节点  n: table的长度  i: 在table的下标  fh: 头节点的hash值
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0) // 尚未初始化
                tab = initTable();
            // 计算出桶下标(n - 1) & hash,通过UnSafe类提供的方法直接操作内存获取对应节点的数据
            // [ConcurrentHashMap中tabAt方法分析](https://blog.youkuaiyun.com/ty649128265/article/details/91857242)
            // 情况1 : 对应桶没有数据, 不加锁, 直接通过比较并交换插入
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            // 情况2:hash值为-1,说明是ForwardingNode节点 这个桶正在被执行扩容
            else if ((fh = f.hash) == MOVED)
            	// 进入帮助扩容流程  后面分析
                tab = helpTransfer(tab, f);
            // 情况3:执行插入
            else {
                V oldVal = null;
                // 锁住头结点f
                synchronized (f) {
                	// 再次检查内存中f这个头结点有没有被修改过
                    if (tabAt(tab, i) == f) {
                    	// 头结点hash码大于零,表示这个桶存的是链表
                        if (fh >= 0) {
                            binCount = 1;
                            // 遍历链表
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                // key 相同 时根据onlyIfAbsent判断是否更新旧值
                                // 通过putIfAbsent这个方法插入数据的场景onlyIfAbsent是true,不允许修改旧值
                                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;
                                // 遍历到链表尾部都没有遇到key相同的,插入到末尾
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        // 桶的头节点是个TreeBin
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                // ==== 插入操作完成==

				// binCount 的双重检查,提高效率
                if (binCount != 0) {
                	// 链表长度超过8触发树化操作
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    
                    // 如果是修改,则返回被修改的原值,否则oldVal就不会被赋值
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }

		// 计数器加1,完成新增后,table扩容,就是这里面触发
        addCount(1L, binCount);
        return null;
    }

addCount方法 计数和触发扩容

先看看HashMap中元素个数的统计

// 由于HashMap使用在单线程的情况下,put插入之后直接元素个数size加1即可
if (++size > threshold) resize();

ConcurrentHashMap为势必需要通过加锁或者自旋来保证并发情况下共享变量的的安全,如果竞争比较激烈的情况下,size的设置上会出现比较大的冲突反而影响了性能,所以在ConcurrentHashMap采用了分片的方法来记录大小,下面通过统计元素个数的sumSize方法来简单看看ConCurrentHashMap是怎么做的

// 标识当前cell数组是否在初始化或扩容中的CAS标志位
private transient volatile int cellsBusy;

/**
 * Base counter value, used mainly when there is no contention,
 * but also as a fallback during table initialization
 * races. Updated via CAS.
 */
private transient volatile long baseCount;

/**
 * Table of counter cells. When non-null, size is a power of 2.
 * counterCells数组,总数值的分值分别存在每个cell中
*/
private transient volatile CounterCell[] counterCells;

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


public int size() {
    long n = sumCount();
    return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ?Integer.MAX_VALUE :
            (int)n);
}
/**
 * CounterCell数组的每个元素,都存储一个元素个数,而实际我们调用size方法就是通过这个循环累加来得到的
*/
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;
    }

通过上面的代码可以看出元素个数同时存储在成员变量baseCount和counterCell(计数盒子)中,put操作插入后无论是baseCount加1还是counterCell的value加1,都相当于完成了元素总数的加1

接着看addCount

/**
 * put完成后调用addCount(1L, binCount);
 * long x : 新增元素个数 1
 * binCount :桶位上元素个数
 */
private final void addCount(long x, int check) {
	// 第一部分 元素总数加x个
    CounterCell[] as; long b, s;
    /*
     * 1. as = counterCells为null时执行 || 后面的逻辑 修改 baseCount
     *    1.1 修改失败(其他线程已经更改了baseCount,CAS修改失败) 进入if块
     * 	  1.2 修改成功 完成元素总数的加1操作
     */
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        CounterCell a; long v; int m;
        boolean uncontended = true; // 这个标志fullAddCount方法中再做说明
        /*
         * 前两个判断:如果计数盒子是空(尚未出现并发)|| 随机取余一个数组位置为空
         * 		true: 说明counterCell还没有创建 直接执行执行fullAddCount(x, true);
         * 		false: 进入第三个判断
         * 第三个判断:随机取出counterCell数组的一个节点
         * 		true: 节点没有存值 直接执行执行fullAddCount(x, true);
         * 		false: 节点有值,进入第四个判断进行修改
         * 第四个判断:修改counterCell的value值
         * 		修改成功: 实现了元素总数加1 不执行fullAddCount
         * 		修改失败: 存在并发,执行fullAddCount(x, false);
         */
        if (as == null || (m = as.length - 1) < 0 ||
        	// ThreadLocalRandom.getProbe() 获取当前线程的探针哈希值,说白了就是获取一个hash值,与上数组长度m,通过简单的散列算法定位一个数组的元素,后面还会用到localInit 初始化和 advanceProbe(h) 更新
            (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended =
              U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
            // 参数 (1, false或者true)
            fullAddCount(x, uncontended);
            // 思考:这里提前返回意味着没有检查是否需要扩容,即对比HashMap,ConCurrentHashMap可能有额外的扩容条件
            return;
        }
        // 删除元素时,会调用addCount(-1L, -1); 这个时候无需检查是否扩容直接返回
        // 当元素插入的空的桶位时,传进来的check为0,
        // 当替换链表的第一个元素的值时,传进来的check为1
        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);
            if (sc < 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))
                transfer(tab, null);
            s = sumCount();
        }
    }
}

再看fullAddCount方法

/**
 * fullAddCount(x, uncontended);
 * x : 需要增加元素个数 1
 * wasUncontended:未冲突标志
 * 		counterCells未创建 传true
 * 		上次CAS修改counterCell失败 传false
 */
private final void fullAddCount(long x, boolean wasUncontended) {
     int h;
     // 为0则代表该线程的ThreadLocalRandom还未初始化
     if ((h = ThreadLocalRandom.getProbe()) == 0) {
         ThreadLocalRandom.localInit();      // force initialization 强制初始化
         // 获取线程哈希值 用于counterCells数组寻址
         h = ThreadLocalRandom.getProbe();
         // addCount方法中尝试了一次修改,失败了才传的false,重新生成哈希值意味着下次取到的counterCell很可能就是另一个,注意:也有可能是同一个
         // 未冲突标志设置为true
         wasUncontended = true;
     }
     boolean collide = false;// True if last slot nonempty
     // 自旋,很显然又有CAS操作了
     for (;;) {
         CounterCell[] as; CounterCell a; int n; long v;
         // counterCells已初始化
         if ((as = counterCells) != null && (n = as.length) > 0) {
         	// 随机取出一个CounterCell元素 等于null 以为意味着数组这个位置的元素没用用过
             if ((a = as[(n - 1) & h]) == null) {
             	 // 成员变量cellsBusy相当于AQS中的同步状态,即可以看成是一个锁,获取锁操作就是cas将其从0改为1
             	 // 0表示counterCells不在初始化或者扩容状态下
                 if (cellsBusy == 0) {            // Try to attach new Cell
                     CounterCell r = new CounterCell(x); // Optimistic create
                     if (cellsBusy == 0 &&
                     	 // 执行成功说明其他线程没有将cellsBusy更改为1,if块中的代码其他线程也进不来
                     	 // 执行失败 进入下一次for循环等待其他线程执行完操作将cellsBusy更改为0
                         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                         boolean created = false;
                         try {               // Recheck under lock
                             CounterCell[] rs; int m, j;
                             // 在cellsBusy保护好的代码再次检查counterCell和其长度,防止其他线程在cellsBusy设置为0之前对counterCell进行更改
                             // 随机取一个数组的元素,如果不为空 又得重新来一次for循环了
                             if ((rs = counterCells) != null && 
                                 (m = rs.length) > 0 &&
                                 rs[j = (m - 1) & h] == null) {
                                 rs[j] = r; // 将创建的CounterCell对象放入寻址位置       
                                 created = true;
                             }
                         } finally {
                             cellsBusy = 0; // 释放锁
                         }
                         if (created) // created == true  说明操作成功,元素增加操作完成,break 溜之大吉
                             break;
                         continue; // Slot is now non-empty // 说明存在竞争该位置已被其他线程放入了CounterCell, 进入下次循环再试,但是下次循环就不一定进得来这里了
                     }
                 }
                 // 设置当前线程的循环失败进行扩容
                 collide = false;
             }
             else if (!wasUncontended)       // CAS already known to fail
                 wasUncontended = true;      // Continue after rehash
             // 由于指定下标位置的cell值不为空,则直接通过cas进行原子累加,如果成功,则直接退出
             else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                 break;
             // 如果已经有其他线程建立了新的counterCells或者CounterCells大于CPU核心数(线程的并发数不会超过cpu核心数)
             else if (counterCells != as || n >= NCPU)
                 //  设置当前线程的循环失败不进行扩容
                 collide = false;            // At max size or stale
             else if (!collide)
                 // 恢复collide状态,标识下次循环会进行扩容
                 collide = true;
             // 进入这个步骤,说明线程竞争较大, CounterCell数组容量不够,所以先设置cellsBusy 标识为1,表示为正在扩容
             else if (cellsBusy == 0 &&
                      U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                 try {
                     if (counterCells == as) {// Expand table unless stale
                         // 扩容一倍,数据迁移至新数组的前半段上,后半段为空
                         CounterCell[] rs = new CounterCell[n << 1];
                         for (int i = 0; i < n; ++i)
                             rs[i] = as[i];
                         counterCells = rs;
                     }
                 } finally {
                     cellsBusy = 0;
                 }
                 collide = false;
                 // 继续下一次自旋
                 continue;                   // Retry with expanded table
             }
             // 每次自旋操作都会生成新的线程哈希值,这样下次获取的CounterCells的下标大概率就不一样了
             h = ThreadLocalRandom.advanceProbe(h);
         }
         /**
          * counterCells的初始化
          * 判断1 cellsBusy==0 表示没有在做初始化,通过cas更新cellsbusy的值标注当前线程正在做初始化操作
          * 判断2 counterCells == as as之前接收counterCells时为空才会进来这个分支
          * 判断3 设置cellsbusy为1,保证分支内的代码只有一个线程执行
          * 
          */
         else if (cellsBusy == 0 && counterCells == as &&
                  U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
             boolean init = false;
             try {                           // Initialize table
             	 // 双重检查,确保counterCells 还是为空
                 if (counterCells == as) {
                 	 // 初始化容量为2
                     CounterCell[] rs = new CounterCell[2];
                     // 增加的元素的个数 放在指定的数组下标位置
                     rs[h & 1] = new CounterCell(x);
                     counterCells = rs;
                     // 设置初始化完成标识为true,作为后面跳出自旋的条件
                     init = true;
                 }
             } finally {
                 cellsBusy = 0;
             }
             if (init)  // 跳出自旋
                 break;
         }
         // 竞争实在是激烈,其它线程占据cell 数组,直接累加在base变量中
         // PS:最开始在addCount方法中在counterCells 为null的情况下也尝试去直接累加到baseCount,如果成功就不会进fullAddCount方法了,这反映了在单线程的场景是不会初始化counterCells,直接累加在baseCount变量上
         else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
             break;                          // Fall back on using base
     }
 }

有两个疑问暂时搞不懂

  1. 如果是addCount方法调用fullAddCount方法完成了元素个数增加,那么就会直接return,这就意味着不会去执行检查扩容的操作,那么元素个数达到阈值时触发扩容这个说法是不是有问题呢?
  2. 在插入到没有元素的桶位时,check参数传0,此时也时直接返回的if (check <= 1) return;不检查扩容了?
private final void addCount(long x, int check) {
	CounterCell[] as; long b, s;
	if ((as = counterCells) != null ||
	    !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
	    CounterCell a; long v; int m;
	    boolean uncontended = true;
	    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();
	}
	// 以下是检查扩容的代码
	...
}

遇到问题阻塞住了,搞明白了再接着看吧。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值