LongAdder源码解析

本文详细解析了AtomicLong的getAndAddLong方法,重点讲解了LongAdder类的longAccumulate方法,通过实例展示了在高并发场景下LongAdder相较于AtomicLong的效率优势。讨论了两者在多线程环境下的应用和LongAdder的内部工作机制,特别是其如何利用 striped64 和线程本地化策略提高性能。
摘要由CSDN通过智能技术生成

AtomicLong

    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

        return var6;
    }
	//比较并替换
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

提高效率 使用 LongAdder

在这里插入图片描述

LongAdder 和 AtomicLong 比较

Demo1:

/**
 * Atomic和LongAdder耗时测试
 */
public class AtomicLongAdderTest {
    public static void main(String[] args) throws Exception{
        testAtomicLongAdder(1, 10000000);
        testAtomicLongAdder(10, 10000000);
        testAtomicLongAdder(100, 10000000);
    }

    static void testAtomicLongAdder(int threadCount, int times) throws Exception{
        System.out.println("threadCount: " + threadCount + ", times: " + times);
        long start = System.currentTimeMillis();
        testLongAdder(threadCount, times);
        System.out.println("LongAdder 耗时:" + (System.currentTimeMillis() - start) + "ms");
        System.out.println("threadCount: " + threadCount + ", times: " + times);
        long atomicStart = System.currentTimeMillis();
        testAtomicLong(threadCount, times);
        System.out.println("AtomicLong 耗时:" + (System.currentTimeMillis() - atomicStart) + "ms");
        System.out.println("----------------------------------------");
    }

    static void testAtomicLong(int threadCount, int times) throws Exception{
        AtomicLong atomicLong = new AtomicLong();
        List<Thread> list = new ArrayList();
        for (int i = 0; i < threadCount; i++) {
            list.add(new Thread(() -> {
                for (int j = 0; j < times; j++) {
                    atomicLong.incrementAndGet();
                }
            }));
        }

        for (Thread thread : list) {
            thread.start();
        }

        for (Thread thread : list) {
            thread.join();
        }

        System.out.println("AtomicLong value is : " + atomicLong.get());
    }

    static void testLongAdder(int threadCount, int times) throws Exception{
        LongAdder longAdder = new LongAdder();
        List<Thread> list = new ArrayList();
        for (int i = 0; i < threadCount; i++) {
            list.add(new Thread(() -> {
                for (int j = 0; j < times; j++) {
                    longAdder.increment();
                }
            }));
        }

        for (Thread thread : list) {
            thread.start();
        }

        for (Thread thread : list) {
            thread.join();
        }

        System.out.println("LongAdder value is : " + longAdder.longValue());
    }
}


结果:

threadCount: 1, times: 10^7
LongAdder 耗时:107ms
AtomicLong 耗时:45ms
----------------------------------------
threadCount: 10, times: 10^7
LongAdder 耗时:58ms
AtomicLong 耗时:831ms
----------------------------------------
threadCount: 100, times: 10^7
LongAdder 耗时:443ms
AtomicLong 耗时:7959ms
----------------------------------------

当线程多了,LongAdder 的优势就体现出来了。

public class LongAdder extends Striped64 implements Serializable {
	   public void add(long x) {
        //as 表示cells 引用
        //b
        //v 表示 期望值表示获取的base值
        //m 表示cells 数组的长度
        //a 表示当前线程命中的 cell单元格
        Cell[] as; long b, v; int m; Cell a;

        //条件一: true-> 表示cells已经初始化过了,当前线程应该将数据写入到对应的cell中
        //  false-> 表示cells 未初始化,当前所有线程应该将数据写入base中


        //条件二:true-> 表示当前线程cas替换数据成功
        //  false-> 表示发生竞争了,可能需要重试 或者 扩容
        //true 是指casBase(b = base, b + x),不是整体,是部分
        //true时,取反是false

        // 初始化过cells 或 发生竞争时需要重试或者扩容 才进入if
        if ((as = cells) != null || !casBase(b = base, b + x)) {

            //true 未竞争      false 发生竞争
            boolean uncontended = true;

            //条件一:true-> 说明 cells 未初始化,也就是多线程写base发生竞争
            //      false-> 说明 cells 已经初始化了,当前线程应该是 找自己的cell 写值
            if (as == null || (m = as.length - 1) < 0 ||

                //条件二:getProbe() 获取当前线程的hash值, m表示 cells长度-1 cells长度 一定是2的次方数 16-1=15=1111 二进制
                //      true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
                //      false-> 说明当前线程对应的cell 不为空,说明 下一步想要将x值,添加到cell中
                (a = as[getProbe() & m]) == null ||

                //条件三:整体而言,有取反,true-> uncontended为false cas操作失败,意味着当前线程对应的cell 有竞争
                //            false-> 表示cas成功
                !(uncontended = a.cas(v = a.value, v + x)))

                //哪些情况会调用
                //true-> 说明 cells 未初始化,也就是多线程写base发生竞争
                //true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
                //true-> cas操作失败,意味着当前线程对应的cell 有竞争
                longAccumulate(x, null, uncontended);
        }
    }


}

重点都在最后一个方法 longAccumulate
是Striped64 类的方法,这个方法是重点

最终方法解析 longAccumulate

abstract class Striped64 extends Number {
  	//1、true-> 说明 cells 未初始化,也就是多线程写base发生竞争
    //2、true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
    //3、true-> cas操作失败,意味着当前线程对应的cell 有竞争
    //longAccumulate(x, null, uncontended);

    //wasUncontended :只有cells 初始化之后,并且当前线程 竞争修改失败,才会是false
    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        // h 表示线程hash值
        int h;
        //条件成立:说明当前线程 还未分配线程
        if ((h = getProbe()) == 0) {
            //给当前线程分配hash值
            ThreadLocalRandom.current(); // force initialization
            //取出当前线程的hash值
            h = getProbe();
            // 因为默认情况下 , 肯定是写入到了 cells[0]位置,不把它当做一次真正的竞争
            //因为没有hash值,所有才会在cells[0]竞争,所有表示一次真正意义上的竞争
            wasUncontended = true;
        }

        //表示扩容意向  false 一定不会扩容,true 可能会扩容
        boolean collide = false;                // True if last slot nonempty

        //自旋
        for (;;) {
            // as 表示cells引用
            // a  表示当前线程命中的cell
            // n  表示cells 数组长度
            // v  表示 期望值
            Cell[] as; Cell a; int n; long v;

            // CASE1:表示cells已经初始化了,当前线程应该将数据写入到对应的cell中
            if ((as = cells) != null && (n = as.length) > 0) {
                //下面两种情况才会进入该if
                //2、true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
                //3、true-> cas操作失败,意味着当前线程对应的cell 有竞争

                // CASE1.1: true->表示当前线程 对应的下标未知的cell 为null,需要创建new Cell
                if ((a = as[(n - 1) & h]) == null) {

                    //true -> 表示当前 锁未被占用
                    if (cellsBusy == 0) {       // Try to attach new Cell

                        //拿当前的x创建Cell
                        Cell r = new Cell(x);   // Optimistically create

                        //条件一:true-> 表示当前锁未被占用 false -> 表示锁被占用
                        //条件二:true-> 表示当前线程获取锁成功, false-> 当前线程获取锁失败
                        if (cellsBusy == 0 && casCellsBusy()) {
                            boolean created = false;
                            try {               // Recheck under lock
                                // rs 表示当前cells引用
                                // m  表示cells长度
                                // j  表示当前线程命中的下标
                                Cell[] rs; int m, j;

                                //条件一 条件二  恒成立
                                //rs[j = (m - 1) & h] == null 是为了防止其它线程初始化 该位置,然后当前线程再次初始化该位置
                                //在上面最近的if 时,其它线程可能抢走锁,让 rs[j]已经赋值,当该线程抢回锁之后可以防止重复赋值
                                if ((rs = cells) != 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
                        }
                    }
                    //扩容意向更改为false      (a = as[(n - 1) & h]) == null
                    //因为当前cell为 null,cells够用,所以不需要扩容
                    collide = false;
                }
                //CASE1.2:
                //只有当前线程的hash值不是0,并且和其它线程一起打在了同一个cell上时,wasUncontended才是false
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash

                //CASE1.3:当前线程rehash 过 hash值,然后新命中的cell不为空
                //true -> 写成功,退出循环
                //false -> 表示rehash之后命中的cell 也有竞争,重试一次
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
                //CASE1.4:
                //条件一:n >= NCPU   true-> 扩容意向为false,表示不扩容了  false-> 表示还可以扩容
                //条件二:cells != as   true -> 其它线程已经扩容过了,当前线程rehash
                else if (n >= NCPU || cells != as)
                    //扩容意向:改为false,表示不扩容了
                    collide = false;            // At max size or stale
                //CASE 1.5:
                //collide取反 设置扩容意向 为true,但不一定扩容
                else if (!collide)
                    collide = true;
                //CASE 1.6:真正的扩容
                // 条件一:cellsBusy == 0 当前线程无锁状态,可以去竞争
                // 条件二:casCellsBusy() 当前线程获取锁,true:可以做扩容逻辑,false:表示其它线程正在做扩容相关操作
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        //cells == as 当其它线程抢了线程,先扩容,当前线程抢回后,可以防止再次扩容
                        // 防止重复扩容
                        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;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                //重置hash值 rehash
                h = advanceProbe(h);
            }

            // CASE2:前置条件cells 还未初始化 as 为 null
            // 条件一:cellsBusy 表示未加锁
            // 条件二:cells == as? 因为其他线程可能会在你给as赋值之后 修改了 cells
            // 条件三:true 表示获取锁成功 会把cellsBusy = ,false表示其他线程正在持有锁
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                //1、true-> 说明 cells 未初始化,也就是多线程写base发生竞争

                boolean init = false;
                try {                           // Initialize table
                    // cells == as 又要对比,防止再次初始化
                    // 防止其他线程已经初始化了,当前线程再次初始化 导致丢失数据
                    if (cells == as) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            // CASE3:
            // 1、当前cellBusy 加锁状态,表示其它线程正在初始化cells,所有当前线程将值累加到base
            // 2、cells被其他线程初始化后,当前线程需要将数据累加到base
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值