ConcurrentHashmap中的size()方法简单解释

来源:互联网 发布:牛贝淘宝客3.17开源版 编辑:程序博客网 时间:2024/05/29 07:48

本文所有的源码都是基于JDK1.8

ConcurrentHashmap中的size()方法源码:

public int size() {        long n = sumCount();        return ((n < 0L) ? 0 :                (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :                (int)n);    }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;    }

根据JDK1.8的注解,当你想大致了解ConcurrentHashmap的容器大小时,建议使用mappingCount()方法。源码如下:

/**     * Returns the number of mappings. This method should be used     * instead of {@link #size} because a ConcurrentHashMap may     * contain more mappings than can be represented as an int. The     * value returned is an estimate; the actual count may differ if     * there are concurrent insertions or removals.     *(大致的意思是:返回容器的大小。这个方法应该被用来代替size()方法,因为     * ConcurrentHashMap的容量大小可能会大于int的最大值。     * 返回的值是一个估计值;如果有并发插入或者删除操作,则实际的数量可能有所不同。)     * @return the number of mappings     * @since 1.8     */    public long mappingCount() {        long n = sumCount();        return (n < 0L) ? 0L : n; // ignore transient negative values    }

其实baseCount就是记录容器数量的,直接放回baseCount不就可以了吗?为什么sumCount()方法中还要遍历counterCells数组,累加对象的值呢?

其中:counterCells是个全局的变量,表示的是CounterCell类数组。CounterCell是ConcurrentHashmap的内部类,它就是存储一个值。

/**     * Table of counter cells. When non-null, size is a power of 2.     */    private transient volatile CounterCell[] counterCells;
/**     * 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; }    }

JDK1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据put()或则删除数据remove()时,会通过addCount()方法更新baseCount:

private final void addCount(long x, int check) {    CounterCell[] as; long b, s;    //U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x) 每次竟来都baseCount都加1因为x=1    if ((as = counterCells) != null ||        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {//1        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))) {            //多线程CAS发生失败的时候执行            fullAddCount(x, uncontended);//2            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);            if (sc < 0) {//如果小于0说明已经有线程在进行扩容操作了                //一下的情况说明已经有在扩容或者多线程进行了扩容,其他线程直接break不要进入扩容操作                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);            }            //这个时候sizeCtl已经等于(rs << RESIZE_STAMP_SHIFT) + 2等于一个大的负数,这边加上2很巧妙,因为transfer后面对sizeCtl--操作的时候,最多只能减两次就结束            else if (U.compareAndSwapInt(this, SIZECTL, sc,                                         (rs << RESIZE_STAMP_SHIFT) + 2))                transfer(tab, null);            s = sumCount();        }    }}

1、初始化时counterCells为空,在并发量很高时,如果存在两个线程同时执行CAS修改baseCount值,则失败的线程会继续执行方法体中的逻辑,执行fullAddCount(x, uncontended)方法,这个方法其实就是初始化counterCells,并将x的值插入到counterCell类中,而x值一般也就是1,这单可以从put()方法中得知。

final V putVal(K key, V value, boolean onlyIfAbsent) {...//1就是要CAS 更新baseCount的值,binCount代表此链表或树的值,一般都大于0.   addCount(1L, binCount);    return null;}

所以counterCells存储的都是value为1的CounterCell对象,而这些对象是因为在CAS更新baseCounter值时,由于高并发而导致失败,最终将值保存到CounterCell中,放到counterCells里。这也就是为什么sumCount()中需要遍历counterCells数组,sum累加CounterCell.value值了。

参考博客:
谈谈 ConcurrentHashMap1.7 和 1.8 的不同实现

原创粉丝点击