简单对比AtomicLong和LongAdder

一、简介

1.AtomicLong

AtomicLong提供了原子性操作long类型数据的解决方案。
我们直到在Java中:

1 byte = 8 bit
1 字节 = 8位

在32位操作系统中,8字节(即:64位)的long和double 变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性。而使用AtomicLong能让long的操作保持原子型。

1.1 AtomicLong源码分析

    @Test
    public void testL() {
        AtomicLong atomicLong = new AtomicLong();
        System.out.println(atomicLong.addAndGet(30L));
    }

运行结果:
在这里插入图片描述
其中addAndGet方法源码如下:使用了CAS
在这里插入图片描述

compareAndSwapInt(this, stateOffset, expect, update)这个方法的作用就是通过cas技术来预测stateOffset变量的初始值是否是expect,如果是,那么就把stateOffset变量的值变成update,如果不是,那么就一直自旋转,一直到stateOffset变量的初始值是expect,然后在在修改stateOffset变量的值变成update。

缺点:
虽然AtomicLong使用CAS算法,但是CAS失败后还是通过无限循环的自旋锁不断的尝试,这就是高并发下CAS性能低下的原因所在。

1.2 AtomicLong常用方法

    @Test
    public void testL() {
        AtomicLong atomicLong = new AtomicLong();
        System.out.println("atomicLong.addAndGet(30L):" + atomicLong.addAndGet(30L));
        System.out.println("atomicLong.get():" + atomicLong.get());

        AtomicLong atomicLong2 = new AtomicLong(20);
        System.out.println("atomicLong2.get():" + atomicLong2.get());

        AtomicLong atomicLong3 = new AtomicLong(10);
        System.out.println("atomicLong3.addAndGet():" + atomicLong3.addAndGet(2));

        AtomicLong atomicLong4 = new AtomicLong(40);
        System.out.println("atomicLong4.getAndAdd():" + atomicLong3.getAndAdd(2));

        //5、如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
        AtomicLong atomicLong5 = new AtomicLong(10);
        atomicLong5.compareAndSet(10, 15);
        System.out.println("atomicLong5 compareAndSet:" + atomicLong5.get());

        AtomicLong atomicLong6 = new AtomicLong(60);
        System.out.println("atomicLong6.getAndIncrement():" + atomicLong6.getAndIncrement());

        AtomicLong atomicLong7 = new AtomicLong(70);
        System.out.println("atomicLong7.incrementAndGet():" + atomicLong7.incrementAndGet());

        AtomicLong atomicLong8 = new AtomicLong(80);
        System.out.println("atomicLong8.incrementAndGet():" + atomicLong8.getAndSet(80));
    }

结果:

atomicLong.addAndGet(30L):30
atomicLong.get():30
atomicLong2.get():20
atomicLong3.addAndGet():12
atomicLong4.getAndAdd():12
atomicLong5 compareAndSet:15
atomicLong6.getAndIncrement()60
atomicLong7.incrementAndGet()71
atomicLong8.incrementAndGet()80

2.LongAdder

LongAdder类是JDK1.8新增的一个原子性操作类。
高并发下N多线程同时去操作一个变量会造成大量线程CAS失败,然后处于自旋状态,导致严重浪费CPU资源,降低了并发性。既然AtomicLong性能问题是由于过多线程同时去竞争同一个变量的更新而降低的,那么如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源。
在这里插入图片描述
LongAdder则是内部维护一个Cells数组,每个Cell里面有一个初始值为0的long型变量,在同等并发量的情况下,争夺单个变量的线程会减少,这是变相的减少了争夺共享资源的并发量,另外多个线程在争夺同一个原子变量时候,如果失败并不是自旋CAS重试,而是尝试获取其他原子变量的锁,最后当获取当前值时候是把所有变量的值累加后再加上base的值返回的。
另外由于Cells占用内存是相对比较大的,所以一开始并不创建,而是在需要时候再创建,也就是惰性加载,当一开始没有空间时候,所有的更新都是操作base变量。

2.1 LongAdder源码分析

在 LongAdder 的父类 Striped64 中存在一个 volatile Cell[] cells; 数组,其长度是2 的幂次方,每个Cell都使用 @Contended 注解进行修饰,而@Contended注解可以进行缓存行填充,从而解决伪共享问题。伪共享会导致缓存行失效,缓存一致性开销变大。
在这里插入图片描述
在这里插入图片描述

value是long类型的占据8个字节,而左边填充七个,右边填充七个, value值在正中间
而一个缓存行是64个字节,所以一个value一定会占用一个缓存行。

伪共享指的是多个线程同时读写同一个缓存行的不同变量时导致的 CPU缓存失效。尽管这些变量之间没有任何关系,但由于在主内存中邻近,存在于同一个缓存行之中,它们的相互覆盖会导致频繁的缓存未命中,引发性能下降。

2.2 LongAdder常用方法

 @Test
    public void testLong() {
        LongAdder longAdder = new LongAdder();
        longAdder.increment();
        System.out.println("longAdder:" + longAdder);

        LongAdder longAdder2 = new LongAdder();
        longAdder2.add(3L);
        System.out.println("longAdder2:" + longAdder2);

        LongAdder longAdder3 = new LongAdder();
        for (int i = 0; i < 10; i++) {
            longAdder3.increment();
        }
        longAdder3.sum();
        System.out.println("longAdder3:" + longAdder3);

        LongAdder longAdder4 = new LongAdder();
        longAdder4.add(5L);
        longAdder4.reset();
        System.out.println("longAdder4:" + longAdder4);


    }

输出结果

longAdder:1
longAdder2:3
longAdder3:10
longAdder4:0

二、AtomicLong和LongAdder对比

LongAddr与AtomicLong的区别

区别AtomicLongLongAdder
原理依靠底层的cas来保障原子性的更新数据热点数据分离为cell数组,
每个数组各自维护自身的值
高并发(线程竞争激励)√(将单点的更新压力分散到各个节点,提高性能)
线程竞争很低√ (简单、高效)
缺点线程竞争激烈时,失败概率很高,性能低在统计的时候如果有并发更新,
可能导致统计的数据有误差
@SpringBootTest
class CommentApplicationTests {
    @Test
    public void testAtomicLong() throws InterruptedException {
        AtomicLong atomicLong = new AtomicLong();
        ArrayList<Thread> list = new ArrayList<>();
        for (int i = 0; i < 9999; i++) {
            list.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 100000; j++) {
                        atomicLong.incrementAndGet();
                    }
                }
            }));
        }
        for (Thread thread : list) {
            thread.start();
        }
        for (Thread thread : list) {
            thread.join();
        }
        System.out.println("AtomicLong value is :" + atomicLong.get());
    }

    @Test
    public void testLongAdder() throws InterruptedException {
        LongAdder longAdder = new LongAdder();
        ArrayList<Thread> list = new ArrayList<>();
        for (int i = 0; i < 9999; i++) {
            list.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 100000; j++) {
                        longAdder.increment();
                    }
                }
            }));
        }

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

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

    @Test
    public void testThread() throws InterruptedException {
        long start = System.currentTimeMillis();
        testLongAdder();
        long end = System.currentTimeMillis();
        System.out.println("LongAdder 用时:" + (end - start) + "毫秒");

        long start1 = System.currentTimeMillis();
        testAtomicLong();
        long end1 = System.currentTimeMillis();
        System.out.println("AtomicLong 用时:" + (end1 - start1) + "毫秒");
    }
}

运行testThread方法后结果如下:
在这里插入图片描述
结论
高并发下LongAdder的用时短,效率高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心向阳光的天域

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值