ConcurrentHashMap源码深入解读

1.针对size方法的讲解,上源码:

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

可以看到源码的size方法,是调用了sumCount方法:

/**
* 设计巧妙的地方就在于,如果只有一个baseCount来储存map的size大小,

* 那么多线程竞争时,势必需要对该变量进行频繁的读写操作,如果直接加

* 锁的话那并发度肯定直接下降,所以只能大师Doug Lea使用了一个CounterCell

* 的内部类的数组:当每个putValue方法去addCount的时候,如果此时存在竞争关系

* 那么线程间会通过一个随机数的hash算法定位到CounterCell[]中某个位置,并将自己

* 添加的元素个数写进去,采用这种方式的设计就充分的将原来从一个baseCount的并发度

* 直接变成了一个数组大小的并发度,这里就是设计巧妙的地方
*/
final long sumCount() {
    //用于并发计算设计的内部类
    CounterCell[] cs = counterCells;
    //volatile型的变量,当程序没有发生竞争时,代表的就是map的size。当发生竞争时,作为
    //一个基础值然后在累加CounterCell数组中的值   
    long sum = baseCount;
    if (cs != null) {
        for (CounterCell c : cs)
            if (c != null)
                sum += c.value;
    }
    return sum;
}

CounterCell内部类如图:

 这里使用了一个Contended的注解,其实就是使用的传说中的64字节填充术,防止cpu cache被

其他变量更改被刷新从而又从主内存读取数据引发的性能降低,因为这里是需要一个cell的数组,

发生竞争时需要分别对不同数组下标的元素进行读写

很多朋友会跟我一样可能搜索很多资料,觉得看到这里就估计结束了,但是本着探索精神凡是要多

问自己几个为什么,怎么实现的呢?那么就接着上述话题,我们直接进入putVal方法调用的

addCount方法具体是怎么玩的:

final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh; K fk; V fv;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                break;                   // no lock when adding to empty bin
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
      //省略中间添加元素的源码部分
    }
    addCount(1L, binCount);
    return null;
}

  /** 
 * Adds to count, and if table is too small and not already
 * resizing, initiates transfer. If already resizing, helps
 * perform transfer if work is available.  Rechecks occupancy
 * after a transfer to see if another resize is already needed
 * because resizings are lagging additions.
 *  看源码中binCount其实就是代表发生hash冲突链上的元素大小
 *   这里特意说明一下
 *  注释翻译过来就是,添加元素,如果table太小并且已经在resizing/初始化
 *  transfer,或者如果已经在resizing尽可能的帮助其进行transfer操作
 *  (这里的尽可能其实就是在最大的一个帮助线程数下进行的65535),因为
 *  添加操作是滞后的,所以重新检查容量需要在transfer之后是为了判断是否
 *  需要进行下一次的resize
 * @param x the count to add
 * @param check if <0, don't check resize, if <= 1 only check if uncontended
 */

采取化繁为简的方式,我们将这个方法先分成两部分看第一部分做了什么:
   

调用fullAddCount方法的条件:

        1.CounterCells存在,并且cell中不存在元素或者存在元素但是cas该cell的value失败
        2.CounterCells不存在,并且cas baseCount失败
    

接着我们继续一探究竟fullAddCount方法:

//因为一张图片截图不了,所以这里直接拿源码来开刷了

    先梳理一下方法主体内容是在干什么:
        a.cell数组存在,如果当前根据hash定位的slot中不存在cell元素,那么创建一个cell并赋值返回方法,但是创建时需要一个cellsbusy的变量该变量用于cell数组resize的flag,值为0时时正常状态,为1时证明有线程在进行初始化或者扩容操作,如果slot中存在cell元素,并且cas该cell的value成功则返回,cas失败则表示此时该slot存在大量竞争,接下来会进行一个cell数组的扩容操作

        b.当cell数组不存在时,cas cellsbusy的flag变量,然后进行counterCell的数组初始化,初始大小为2,初始化的同时直接通过hash定位slot将value值设置到定位的cell中,直接返回

        c.当a、b都没执行成功,那么此时就会直接进行baseCount变量的cas的兜底操作   

private final void fullAddCount(long x, boolean wasUncontended) {
    int h;
    if ((h = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();      // force initialization
        h = ThreadLocalRandom.getProbe();
        wasUncontended = true;
    }
    boolean collide = false;                // True if last slot nonempty
    for (;;) {
        CounterCell[] cs; CounterCell c; int n; long v;
        if ((cs = counterCells) != null && (n = cs.length) > 0) {
            if ((c = cs[(n - 1) & h]) == null) {
                if (cellsBusy == 0) {            // Try to attach new Cell
                    CounterCell r = new CounterCell(x); // Optimistic create
                    if (cellsBusy == 0 &&
                        U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
                        boolean created = false;
                        try {               // Recheck under lock
                            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;           // Slot is now non-empty
                    }
                }
                collide = false;
            }
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash
            else if (U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))
                break;
            else if (counterCells != cs || n >= NCPU)
                collide = false;            // At max size or stale
            else if (!collide)
                collide = true;
            else if (cellsBusy == 0 &&
                     U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
                try {
                    if (counterCells == cs) // Expand table unless stale
                        counterCells = Arrays.copyOf(cs, n << 1);
                } finally {
                    cellsBusy = 0;
                }
                collide = false;
                continue;                   // Retry with expanded table
            }
            h = ThreadLocalRandom.advanceProbe(h);
        }
        else if (cellsBusy == 0 && counterCells == cs &&
                 U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
            boolean init = false;
            try {                           // Initialize table
                if (counterCells == cs) {
                    CounterCell[] rs = new CounterCell[2];
                    rs[h & 1] = new CounterCell(x);
                    counterCells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            if (init)
                break;
        }
        else if (U.compareAndSetLong(this, BASECOUNT, v = baseCount, v + x))
            break;                          // Fall back on using base
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值