LongAdder

316 阅读4分钟

LongAdder是一个线程安全的记数器,相比AtomicLong在高并发下有更好的性能。

LongAdder关键的方法是add。

add

add方法首先判断当前add操作是否符合cell是空、base执行CAS成功,通常情况下,都会对base执行相加,在并发比较高的情况下,CAS可能失败,就会进入里面代码块。

里面代码块判断如果满足4个条件任意1个,就执行下面的longAccumulate方法

  • as是空,无法加上值,需要进入longAccumulate初始化
  • as长度小于等于0,无法加上值,需要进入longAccumulate初始化
  • 本次操作对应的槽是空,无法加上值,需要进入longAccumulate初始化
  • 对槽指行CAS操作失败,需要进入longAccumulate自旋加上值

public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;
    if ((as = cells) != null || !casBase(b = base, b + x)) {
        boolean uncontended = true;
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[getProbe() & m]) == null ||
            !(uncontended = a.cas(v = a.value, v + x)))
            longAccumulate(x, null, uncontended);
    }
}

longAccumulate执行的操作分别是

  • 判断getProbe返回结果是否是0,如果是0,则说明ThreadLocalRandom还没有初始化,这时会进行初始化,并调用getProbe获取probe值
  • 如果有cell,判断本次操作对应的槽是否是空,如果是空,判断cellsBusy是否是0,如果是,说明当前没有线程在操作cell,可以执行操作,这里用到了DCL,如果DCL成功,就进入里面代码块将当前槽增加1个cell,如果执行成功,要添加的值就被加上了,Cell对象入参;wasUncontended这个分支没看懂是在干嘛;然后第3个分支执行CAS操作,这里如果有fn,函数接口的实现,就会用函数接口的实现计算值,否则直接相加计算值;第4个分支判断槽位数是否大于CPU数、或者cell数组是否修改,如果满足1个条件,则说明不允许扩容;第5个分支判断是否允许扩容,如果不允许,改为允许;第6个分支判断cellsBusy是否等于0,表示是否有别的线程在操作,如果没有,则进行扩容
  • 执行到for循环第2个大分支,表示cell是空,初始化下cell
  • 前2个大分支都执行失败,再试下base执行CAS操作
  • 如果都失败,自旋重试一下

final void longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended) {
    int h;
    if ((h = getProbe()) == 0) {//判断当前线程的probe值是否被初始化
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
        wasUncontended = true;
    }
    boolean collide = false;                // True if last slot nonempty
    for (;;) {
        Cell[] as; Cell a; int n; long v;
        if ((as = cells) != null && (n = as.length) > 0) {//判断是否有cell
            if ((a = as[(n - 1) & h]) == null) {//判断本次操作对应的槽是否是空
                if (cellsBusy == 0) {       // 第1次check
                    Cell r = new Cell(x);   
                    if (cellsBusy == 0 && casCellsBusy()) {//加锁
                        boolean created = false;
                        try {               
                            Cell[] rs; int m, j;
                            if ((rs = cells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {//第2次check
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            cellsBusy = 0;
                        }
                        if (created)
                            break;
                        continue;//没有重新生成随机数           
                    }
                }
                collide = false;
            }
            else if (!wasUncontended)       
                wasUncontended = true;      
            else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                         fn.applyAsLong(v, x))))//如果有函数接口,用函数接口计算值,否则之间相加计算值
                break;
            else if (n >= NCPU || cells != as)//判断槽位数是否大于CPU数,或者槽位已经扩容,如果大于等于或者已经扩容,不允许扩容
                collide = false;            // At max size or stale
            else if (!collide)//如果不允许扩容,改成允许扩容
                collide = true;
            else if (cellsBusy == 0 && casCellsBusy()) {//如果没有别的线程在操作,就执行扩容操作
                try {
                    if (cells == as) {      
                        Cell[] rs = new Cell[n << 1];
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        cells = rs;
                    }
                } finally {
                    cellsBusy = 0;
                }
                collide = false;
                continue;//没有重新生成随机数,因为扩容了,要做一次尝试                   
            }
            h = advanceProbe(h);//获取新的随机数
        }
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {//cell是空,生成1个cell数组,添加要加的值
            boolean init = false;
            try {                           // Initialize table
                if (cells == as) {
                    Cell[] rs = new Cell[2];
                    rs[h & 1] = new Cell(x);
                    cells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            if (init)
                break;
        }
        else if (casBase(v = base, ((fn == null) ? v + x :
                                    fn.applyAsLong(v, x))))//上面都执行失败,重新试一下base变量的CAS操作
            break;                          
    }
}

Cell

上面一直用到Cell是Striped64内部类。

Contended表示这个对象会被填充值,防止因为缓存行导致的伪共享影响性能。

valueOffset是Cell的变量value在类里的位置,因为CAS是方法操作,java方法原生类型是传值,对象是传引用,所以不能直接传入value进去,需要用这种方式修改变量的值,类似c的指针,或者底层就是用c的指针实现的。

@sun.misc.Contended static final class Cell {
    volatile long value;
    Cell(long x) { value = x; }
    final boolean cas(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
    }
    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long valueOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> ak = Cell.class;
            valueOffset = UNSAFE.objectFieldOffset
                (ak.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

sum

最后看一下sum方法,获取总数。

不出意外,是base的值加上所有cell的值。

public long sum() {
    Cell[] as = cells; Cell a;
    long sum = base;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
}