当前位置:   article > 正文

简单对比AtomicLong和LongAdder_longadder 和atomiclong

longadder 和atomiclong

一、简介

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));
    }
  • 1
  • 2
  • 3
  • 4
  • 5

运行结果:
在这里插入图片描述
其中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));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

结果:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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);


    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

输出结果

longAdder:1
longAdder2:3
longAdder3:10
longAdder4:0
  • 1
  • 2
  • 3
  • 4

二、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) + "毫秒");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

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

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号