zoukankan      html  css  js  c++  java
  • 关于LongAdder

    一.LongAdder是什么

    JDK1.8时,java.util.concurrent.atomic包中提供了一个新的原子类:LongAdder。提供了原子累计值的方法。

    根据Oracle官方文档的介绍,LongAdder在高并发的场景下会比它的前辈————AtomicLong 具有更好的性能,代价是消耗更多的内存空间:

    二.LongAdder能做什么

    在并发量较低的环境下,线程冲突的概率比较小,自旋的次数不会很多。但是,高并发环境下,N个线程同时进行自旋操作,会出现大量失败并不断自旋的情况,此时AtomicLong的自旋会成为瓶颈。

    这就是LongAdder引入的初衷——解决高并发环境下AtomicLong的自旋瓶颈问题。

    在大数据处理过程,为了方便监控,需要统计数据,少不了原子计数器。为了尽量优化性能,需要采用高效的原子计数器。

    在jdk8中,引入了LongAddr,非常适合多线程原子计数器。与AtomicLong做了一个测试,LongAdder在多线程环境中,原子自增长性能要好很多。它常用于状态采集、统计等场景。

    三.LongAdder原理

    LongAdder的原理是,在最初无竞争时,只更新base的值,当有多线程竞争时通过分段的思想,让不同的线程更新不同的段,最后把这些段相加就得到了完整的LongAdder存储的值。

    AtomicLong的如图:

     LongAdder的如图:

    AtomicLong中有个内部变量value保存着实际的long值,所有的操作都是针对该变量进行。也就是说,高并发环境下,value变量其实是一个热点,也就是N个线程竞争一个热点。

    LongAdder的基本思路就是分散热点,将value值分散到一个数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,

    这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

    AtomicLong是多个线程针对单个热点值value进行原子操作。而LongAdder是每个线程拥有自己的槽,各个线程一般只对自己槽中的那个值进行CAS操作。

    LongAdder继承自Striped64抽象类,Striped64中定义了Cell内部类和各重要属性。

    唯一会制约AtomicLong高效的原因是高并发,高并发意味着CAS的失败几率更高, 重试次数更多,越多线程重试,CAS失败几率又越高,变成恶性循环,AtomicLong效率降低。

    那怎么解决? LongAdder给了一个非常容易想到的解决方案:减少并发,将单一value的更新压力分担到多个value中去,降低单个value的 “热度”,分段更新。

    这样,线程数再多也会分担到多个value上去更新,只需要增加value就可以降低 value的 “热度” ,AtomicLong中的 恶性循环就解决了。

    cells 就是这个 “段” cell中的value 就是存放更新值的, 这样,当需要总数时,把cells 中的value都累加一下就可以了。

    四.LongAdder使用

    LongAdder VS AtomicLong:

    代码如下

    public class LongAdderVSAtomicLongTest {
    public static void main(String[] args){ testAtomicLongVSLongAdder(1, 10000000); testAtomicLongVSLongAdder(10, 10000000); testAtomicLongVSLongAdder(20, 10000000); testAtomicLongVSLongAdder(40, 10000000); testAtomicLongVSLongAdder(80, 10000000); } static void testAtomicLongVSLongAdder(final int threadCount, final int times){ try { System.out.println("threadCount:" + threadCount + ", times:" + times); long start = System.currentTimeMillis(); testLongAdder(threadCount, times); System.out.println("LongAdder elapse:" + (System.currentTimeMillis() - start) + "ms"); long start2 = System.currentTimeMillis(); testAtomicLong(threadCount, times); System.out.println("AtomicLong elapse:" + (System.currentTimeMillis() - start2) + "ms"); } catch (InterruptedException e) { e.printStackTrace(); } } static void testAtomicLong(final int threadCount, final int times) throws InterruptedException { 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(); } } static void testLongAdder(final int threadCount, final int times) throws InterruptedException { 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.add(1); } })); } for (Thread thread : list){ thread.start(); } for (Thread thread : list){ thread.join(); } } }

    运行结果如下:

    threadCount:1, times:10000000
    LongAdder elapse:158ms
    AtomicLong elapse:64ms
    threadCount:10, times:10000000
    LongAdder elapse:206ms
    AtomicLong elapse:2449ms
    threadCount:20, times:10000000
    LongAdder elapse:429ms
    AtomicLong elapse:5142ms
    threadCount:40, times:10000000
    LongAdder elapse:840ms
    AtomicLong elapse:10506ms
    threadCount:80, times:10000000
    LongAdder elapse:1369ms
    AtomicLong elapse:20482ms

    可以看到当只有一个线程的时候,AtomicLong反而性能更高,随着线程越来越多,AtomicLong的性能急剧下降,而LongAdder的性能影响很小。

    总结:

    (1)LongAdder通过base和cells数组来存储值;

    (2)不同的线程会hash到不同的cell上去更新,减少了竞争;

    (3)LongAdder的性能非常高,最终会达到一种无竞争的状态;

  • 相关阅读:
    leetcode 309. Best Time to Buy and Sell Stock with Cooldown
    leetcode 714. Best Time to Buy and Sell Stock with Transaction Fee
    leetcode 32. Longest Valid Parentheses
    leetcode 224. Basic Calculator
    leetcode 540. Single Element in a Sorted Array
    leetcode 109. Convert Sorted List to Binary Search Tree
    leetcode 3. Longest Substring Without Repeating Characters
    leetcode 84. Largest Rectangle in Histogram
    leetcode 338. Counting Bits
    git教程之回到过去,版本对比
  • 原文地址:https://www.cnblogs.com/ZJOE80/p/12917940.html
Copyright © 2011-2022 走看看