LongAdder浅析

239 阅读4分钟

一、介绍

​ LongAdder是Doug Lea设计的一个线程安全的累加器,基本可以替换AtomicLong.其设计主要分为两部分,一部分是base,基础数据,如果没有数据竞争,那么就一直会对base进行cas累加;另一部分就是cell数组,该目的就是分担base的压力.举个例子,如果有一百个线程需要数据累加,AtomicLong只会一个线程去累加,其余线程自旋重试,而LongAdder可以有cell数组长度+1个线程同时累加,消费者增加,处理更快;

二、继承结构

核心的cell,base等概念,以及累加的核心逻辑,都是在striped64,下面会介绍

三、源码分析

LongAdder的入口主要是add方法

public void add(long x) {
        //as cell数组的引用
        // b 就是base的引用
        // v 当前线程对应的cell的值
        //m cell数组的长度
        // a 当前线程对应的cell
        Cell[] as; long b, v; int m; Cell a;
        // cell初始化过了
        //cas base值失败
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            // 就是当前cell对应的cas操作是否成功,默认true
            boolean uncontended = true;
            //cell数组未初始化
            if (as == null || (m = as.length - 1) < 0 ||
                //当前线程对应的cell未初始化
                (a = as[getProbe() & m]) == null ||
                // 给当前cell cas加上一个值
                !(uncontended = a.cas(v = a.value, v + x)))
                //累加的核心累加,由父类striped64实现
                longAccumulate(x, null, uncontended);
        }
    }

LongAdder的取值代码很简单,就是将base的值和cell数组所有元素的值;

public long sum() {
    //as cell数组的引用
    //a cell数组其中的桶位引用
    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;
}

striped64的核心属性

    @sun.misc.Contended static final class Cell {
        //cell的值,用于最终结果的累加
        volatile long value;
        Cell(long x) { value = x; }
        //cas操作,给value叠加val值
        final boolean cas(long cmp, long val) {
            return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
        }
        // Unsafe mechanics unsafe类的引用
        private static final sun.misc.Unsafe UNSAFE;
        //value的偏移量
        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);
            }
        }
    }

    /** Number of CPUS, to place bound on table size */
    /**
     * cpu核心数,是cell数组length的最大值
     */
    static final int NCPU = Runtime.getRuntime().availableProcessors();

    /**
     * Table of cells. When non-null, size is a power of 2.
     */
    /**
     * cell数组
     */
    transient volatile Cell[] cells;

    /**
     * Base value, used mainly when there is no contention, but also as
     * a fallback during table initialization races. Updated via CAS.
     */
    transient volatile long base;

    /**
     * Spinlock (locked via CAS) used when resizing and/or creating Cells.
     */
    /**
     * 0表示未加锁
     * 1表示加锁
     */
    transient volatile int cellsBusy;

longAccumulate累加器

//这里的fn是一个函数式接口,可以由开发者处理,但是longadder设置为null
final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        //当前线程的hash值
        int h;
    	//新线程默认hash值为0
        if ((h = getProbe()) == 0) {
            //设置一个为该线程设置一个hash值
            ThreadLocalRandom.current(); // force initialization
            h = getProbe();
            //
            wasUncontended = true;
        }
        //就是代表着cell数组是否需要扩容,默认不需要
        boolean collide = false;                // True if last slot nonempty
        //死循环给cell设置value或累加value
    	for (;;) {
            //as 还是cell数组的引用
            //a 当前线程hash对应的桶位
            //n是数组长度
            // v 就是a的值
            Cell[] as; Cell a; int n; long v;
            //cell数组已经初始化了
            if ((as = cells) != null && (n = as.length) > 0) {
                //当前线程hash对应的桶位未初始化
                //与该条件同级别的条件还有5个,只要满足一个(除了break出去的),都会更改hash值,从而重新选择				//桶位
                if ((a = as[(n - 1) & h]) == null) {
                    //未加锁
                    //cellsBusy为1的情况有以下几种,其他线程遇到了会自旋等待这些操作结束(或者给base累加						完)
                    //(1)初始化cell数组
                    //(2)初始化单个cell
                    //(3) cell数组扩容
                    if (cellsBusy == 0) {
                        // Try to attach new Cell
                        Cell r = new Cell(x);   // Optimistically create
                        //未加锁,cas获取锁成功
                        if (cellsBusy == 0 && casCellsBusy()) {
                            //设置创建标志位
                            boolean created = false;
                            try {               // Recheck under lockrs
                                //
                                Cell[] rs; int m, j;
                                //doublechcek逻辑,该逻辑中使用了大量的doublecheck,以防止多线程情                                //况下数据不准 cell数组为不为空
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    //当前cell桶位是不是为空
                                    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;
                }
                //之前cas cell值失败
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                //使用cas 给cell累加x
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
				//如果当前cell数组长度大于核心线程数,则不扩容
                //cells != as 是doublecheck,防止其他线程已经扩容
                else if (n >= NCPU || cells != as)
                    collide = false;            // At max size or stale
				//不允许扩容,则设置扩容标记位为true
                else if (!collide)
                    collide = true;
                //未加锁且请求到锁
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        //doublecheck,防止其他线程已经扩容
                        if (cells == as) {      // Expand table unless stale
                            //扩容为原来的两倍
                            Cell[] rs = new Cell[n << 1];
                            //将原数组内容赋值给新数组
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        //释放锁
                        cellsBusy = 0;
                    }
                    //扩容标记位设为false
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                h = advanceProbe(h);
            }
            //cell数组未初始化,未加锁,且未初始化,且加锁成功
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                //初始化标记位
                boolean init = false;
                try {                         // Initialize table
                    //doublecheck,,防止其他线程已经初始化cell数组
                    if (cells == as) {
                        //初始化长度2
                        Cell[] rs = new Cell[2];
                        //初始化其中一个cell
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    //释放锁
                    cellsBusy = 0;
                }
                //跳出死循环
                if (init)
                    break;
            }
            //给base值加上x
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }

四:总结

​ LongAdder采取分而治之的思想减少高并发下,cas自旋导致的性能开销