ConcurrentHashMap源码分析之计数:addCount、fullAddCount、size

ConcurrentHashMap(以下简称CHM)的计数比较有特色,跟很多实现不同的是,CHM并没有一个专门的属性数据,在实现上也没有用锁来同步。那么它到底是怎么实现的呢?

CHM计数的总体原理

CHM在实现计数时,主要借助的是baseCount和counterCells(数组)两个属性。

  • baseCount是原生类型long,默认值0L
  • baseCount在首次添加元素的时候设置为1
  • 视并发情况的不同,可能在addCount中赋值,也可能在fullAddCount中赋值,且仅赋值一次
  • counterCells数组的长度也是2的幂
  • 每个线程根据其probe的值获取对应的counterCells下标
  • 每个线程只会更新对应数组元素中的数据
  • 求size时,就是baseCount + counterCells计数之和

【注意1】counterCells之所以不用基本类型(Long,Integer等)计数,而是用CounterCell对象,是因为要用volatile。而volatile修饰数组,只对数组的引用有效,而对数组中的元素无效。所以才用数组存储CounterCell对象,而在CounterCell对象中用volatile计数。

【注意2】CounterCell类声明时使用了@sun.misc.Contended注解,避免伪共享。该注解会在对象的前后各添加128字节,以避免同临近数据一起被加入到同一L1缓存行中。

源码

private final void addCount(long x, int check) {
    CounterCell[] as; long b, s;
    // 如果计数数组未初始化,就设置baseCount
    // 如果已初始化,或初始化时设置baseCount“竞争”失败,
    // 则通过计数数组计数
    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();
    }
    if (check >= 0) {
    // 如下扩容部分暂且省略,留待讲扩容的时候再讲
    。。。
}

// x:当前线程计数的增加值
// wasUncontended:线程计数竞争counterCell时是否失败。其实调用fullAddCount时必然竞争失败,
// 所以wasUnconteded肯定是false
private final void fullAddCount(long x, boolean wasUncontended) {
        int h;
        // 初始化当前线程的探针,也就是线程的HashCode。为了避免冲突,线程的探针可以修改,通过advanceProbe方法
        if ((h = ThreadLocalRandom.getProbe()) == 0) {
            ThreadLocalRandom.localInit();      // force initialization
            h = ThreadLocalRandom.getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        for (;;) {
            CounterCell[] as; CounterCell a; int n; long v;
            // 如果counterCells已经初始化过
            if ((as = counterCells) != null && (n = as.length) > 0) {
                // 且当前线程的counterCell未使用过
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) {            // Try to attach new Cell
                        CounterCell r = new CounterCell(x); // Optimistic create
                        if (cellsBusy == 0 &&
                            U.compareAndSwapInt(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.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                    break;
                else if (counterCells != as || n >= NCPU)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                // 尝试扩容counterCells
                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
                }
                // 如果目标counterCell已被占用,且设置失败,也在竞争扩容时失败,则修改当前线程的PROBE
                h = ThreadLocalRandom.advanceProbe(h);
            }
            // 尝试初始化counterCells
            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;
            }
            // counterCells未初始化,当前线程也在竞争初始化的机会时失败,则设置baseCount。
            // 这是第2/2次设置baseCount的机会
            else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
                break;                          // Fall back on using base
        }
    }

// CHM的size直接调用的是sumCount,只是对结果最后又做了一层校验而已。
// sumCount其实就是baseCount + 各个counterCell里的值
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;
    }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值