ConcurrentHashMap源码解析

当一件事你不能说明白的时候,十有八九是你还不是十分了解(自勉)

base on JDK 1.8.0_60

首先我们来看看ConcurrentHashMap整体结构,如图所示


ConcurrentHashMap.png

ConcurrentHashMap是如何初始化?

首先我们来看看ConcurrentHashMap关于初始化比较重要的属性

    /**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    transient volatile Node[] table;

    /**
     * Table initialization and resizing control.  When negative, the
     * table is being initialized or resized: -1 for initialization,
     * else -(1 + the number of active resizing threads).  Otherwise,
     * when table is null, holds the initial table size to use upon
     * creation, or 0 for default. After initialization, holds the
     * next element count value upon which to resize the table.
     */
    private transient volatile int sizeCtl;

下面我们来看看ConcurrentHashMap中的关于初始化的代码initTable()方法

    /**
     * Initializes table, using the size recorded in sizeCtl.
     */
    private final Node[] initTable() {
        Node[] tab; int sc;
        //如果表为空才进行初始化工作
        while ((tab = table) == null || tab.length == 0) {
        //如果sizeCtl小于为负数则说明有线程正在进行初始化操作
        //当前线程应该放弃CPU的使用
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
             //否则说明尚未有线程对表进行初始化,那么本线程就来做这个工作   
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
               //保险起见,再次判断下表是否为空
                    if ((tab = table) == null || tab.length == 0) {
                      //至此, sc 大于零说明容量已经初始化了,否则使用默认容量,其他线程再也无法初始化!!!
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        //构建容量数组
                        Node[] nt = (Node[])new Node[n];
                        table = tab = nt;                       
                        //计算阈值,等效于 n*0.75
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

ConcurrentHashMap是如何统计元素数量?

首先我们来看看ConcurrentHashMap关于元素数量的辅助属性与类

    /**
     * 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;

    /**
     * A padded cell for distributing counts.  Adapted from LongAdder
     * and Striped64.  See their internal docs for explanation.
     */
    @sun.misc.Contended static final class CounterCell {
        volatile long value;
        CounterCell(long x) { value = x; }
    }

下面我们来看看CouncurrentHashMap的size()方法,其中主要调用了sumCount()方法

    /**
     * {@inheritDoc}
     */
    public int size() {
        long n = sumCount();
        return ((n < 0L) ? 0 :
                (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
                (int)n);
    }

下面是sunCount()的源码,可以看出ConcurrentHashMap就是通过计算baseCount与CountCell数组的值来计算ConcurrentHashMap包含的元素的数量,这就引发了一个思考,为什么ConcurrentHashMap要这样设计来统计包含的元素数量?


    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;
    }

未完待续

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