首页 > 其他分享 >LongAdder 比 AtomicLong 性能更好

LongAdder 比 AtomicLong 性能更好

时间:2024-07-17 17:30:03浏览次数:19  
标签:LongAdder AtomicLong 更好 线程 CountDownLatch new public

一、LongAdder入门

API文档地址:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html

构造方法:初始值为0

LongAdder()

方法:

add(long x)
decrement() // 减1
increment() // 加1
toString()

二、LongAdder于AtomicLong性能比较

通过案例比较synchronized、AtomicInteger、AtomicLong、LongAdder、LongAccumulator五种计数性能

ClickNumber类

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

public class ClickNumber{
    int number = 0;
    public synchronized void add_synchronized(){
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();
    public void add_AtomicInteger(){
        atomicInteger.incrementAndGet();
    }

    AtomicLong atomicLong = new AtomicLong();
    public void add_AtomicLong(){
        atomicLong.incrementAndGet();
    }

    LongAdder longAdder = new LongAdder();
    public void add_LongAdder(){
        longAdder.increment(); // 加1
    }

    LongAccumulator longAccumulator = new LongAccumulator((x, y)->x+y,0);
    public void add_longAccumulator(){
        longAccumulator.accumulate(1);
    }
}

测试类

import java.util.concurrent.CountDownLatch;

public class LongAdderCalcDemo {
    public static final int SIZE_THREAD = 50; // 50个线程
    public static final int _1w = 10000;

    public static void main(String[] args) throws InterruptedException {
        ClickNumber clickNumber = new ClickNumber();
        long startTime = System.currentTimeMillis();
        long endTime = System.currentTimeMillis();
        // CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。
        // 当计数器值到达0时,它表示所有的线程已经完成了任务,然后就可以恢复等待的线程继续执行了。
        CountDownLatch latch_synchronized = new CountDownLatch(SIZE_THREAD);
        CountDownLatch latch_AtomicInteger= new CountDownLatch(SIZE_THREAD);
        CountDownLatch latch_AtomicLong = new CountDownLatch(SIZE_THREAD);
        CountDownLatch latch_LongAdder = new CountDownLatch(SIZE_THREAD);
        CountDownLatch latch_LongAccumulator = new CountDownLatch(SIZE_THREAD);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= SIZE_THREAD; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100*_1w; j++) {
                        clickNumber.add_synchronized();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch_synchronized.countDown(); // 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
                }
            },String.valueOf(i)).start();
        }
        latch_synchronized.await(); // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
        endTime = System.currentTimeMillis();
        System.out.println("synchronized花费时间:"+ (endTime-startTime)+" 数值为:"+clickNumber.number);


        startTime = System.currentTimeMillis();
        for (int i = 1; i <= SIZE_THREAD; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100*_1w; j++) {
                        clickNumber.add_AtomicInteger();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch_AtomicInteger.countDown();
                }
            },String.valueOf(i)).start();
        }
        latch_AtomicInteger.await();
        endTime = System.currentTimeMillis();
        System.out.println("AtomicInteger花费时间:"+ (endTime-startTime)+" 数值为:"+clickNumber.atomicInteger.get());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= SIZE_THREAD; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100*_1w; j++) {
                        clickNumber.add_AtomicLong();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch_AtomicLong.countDown();
                }
            },String.valueOf(i)).start();
        }
        latch_AtomicLong.await();
        endTime = System.currentTimeMillis();
        System.out.println("AtomicLong花费时间:"+ (endTime-startTime)+" 数值为:"+clickNumber.atomicLong.get());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= SIZE_THREAD; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100*_1w; j++) {
                        clickNumber.add_LongAdder();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch_LongAdder.countDown();
                }
            },String.valueOf(i)).start();
        }
        latch_LongAdder.await();
        endTime = System.currentTimeMillis();
        System.out.println("LongAdder花费时间:"+ (endTime-startTime)+" 数值为:"+clickNumber.longAdder.longValue());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= SIZE_THREAD; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100*_1w; j++) {
                        clickNumber.add_longAccumulator();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    latch_LongAccumulator.countDown();
                }

            },String.valueOf(i)).start();
        }
        latch_LongAccumulator.await();
        endTime = System.currentTimeMillis();
        System.out.println("LongAccumulator花费时间:"+ (endTime-startTime)+" 数值为:"+clickNumber.longAccumulator.longValue());

    }
}

结果:

synchronized花费时间:2483 数值为:50000000
AtomicInteger花费时间:653 数值为:50000000
AtomicLong花费时间:761 数值为:50000000
LongAdder花费时间:96 数值为:50000000
LongAccumulator花费时间:102 数值为:50000000

通过结果,可知LongAdder性能最优,花费时间最短,远优于AtomicLong.

LongAdder为何那么快?

LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

LongAdder在无竞争的情况,跟AtomicLong一样,对同一个base进行操作(无并发,单线程下直接CAS操作更新base值;非竞态条件下,直接累加到变量base上)

当出现竞争关系时则是采用化整为零的做法,从空间换时间,用一个数组cells,将一个value拆分进这个数组Cells.多个线程需要同时对value进行操作时,可以对线程id进行hash得到hash值,再根据hash值映射到这个数组cells的某个下标,再对该下标所对应的值进行自增操作。当所有线程操作完毕,将数组cells的所有值和无竞争值base都加起来作为最终结果。(有并发,多线程下分段CAS操作更新Cell数组值;竞态条件下,累加个各个线程自己的槽Cell[]中)

 

与AtomicLong对比

1、AtomicLong

原理:CAS + 自旋,

场景:低并发下的全局计算,AlomicLong能保证并发情况下计数的准确性,其内部通过CAS来解决并发安全性的问题。可允许一些性能损耗,要求高精度时可使用。AtomicLong是多个线程针对单个热点值value进行原子操作

缺陷:高并发后性能急剧下降。(N个线程CAS操作修改线程的值,每次只有一个成功过,其它N-1失败,失败的不停的自旋直到成功,这样大量失败自旋的情况,占用大量CPU)

2、LongAdder

原理:CAS+Base+Cell数组分散,通过空间换时间分散了热点数据

场景:高并发下的全局计算,当需要在高并发下有较好的性能表现,且对值的精确度要求不高时,可以使用。LongAdder是每个线程拥有自己的槽,各个线程一般只对自己槽中的那个值进行CAS操作

缺陷:sum求和后还有计算线程修改结果的话,最后结果不够准确

 

标签:LongAdder,AtomicLong,更好,线程,CountDownLatch,new,public
From: https://www.cnblogs.com/zwh0910/p/17346932.html

相关文章

  • 如何更好的优化 ListView 控件的性能
    ......
  • BBdown -- 更好的B站视频下载软件
    来自......
  • HTTP协议27丨更好更快的握手:TLS1.3特性解析
    上一讲中我讲了TLS1.2的握手过程,你是不是已经完全掌握了呢?不过TLS1.2已经是10年前(2008年)的“老”协议了,虽然历经考验,但毕竟“岁月不饶人”,在安全、性能等方面已经跟不上如今的互联网了。于是经过四年、近30个草案的反复打磨,TLS1.3终于在去年(2018年)“粉墨登场”,再......
  • Python——比 Seaborn 更好的相关性热力图:Biokit Corrplot
    目录前言:我们需要更好的相关性热力图对比PythonSeaborn与Rcorrplot传统的Seaborn相关性热力图R语言中的相关性热力图关于Biokit简介库的安装相关性热图的绘制基本使用方法详述一些绘图参数的问题及细节关于order_method参数关于order_metric参数关于cmap参数改进B......
  • PixPlant.5.0.42_x64破解汉化版,创建平铺 3D 材质的更好方法
    PixPlant5是一款功能强大的贴图生成器,可用于快速生成高质量的纹理贴图,下面分享PixPlant5贴图生成器的使用方法。1、在网上找到需要的贴图类型,如丝绸,木板,砖块,纺织物等任意需要的贴图。2、找到贴图后打开PS,将贴图设置为长宽相同尺寸的图片,如以下设置为500×500像素,如贴图使......
  • 【产品经理修炼之道】- 登录页怎么更好地吸引用户
    编辑导语:登录页对于一个网站来说十分重要,是用户对该网站的第一印象。本文作者分享了登录页更好地吸引用户的具体方法和思路,讲述了更好登录页文案的快捷方案和有效登录页文案的基本要素等,一起来学习一下吧。网站的开头都有个登录页。这往往是对公司的第一印象:是一个组织,它要......
  • 使用Ventoy 替代Win_To_Go更好的随身系统
    Ventoy支持在物理机上直接启动安装了Linux/Windows 系统的磁盘映像文件。系统是在真实物理机上运行,并不是在虚拟机里运行,没有性能损失。支持LegacyBIOS和UEFI模式。支持从任意磁盘启动磁盘映像。Windows 支持固定大小以及动态扩展类型的VHD/VHDX格式(Win7以上)。......
  • 1对1视频聊天源码,优化后的缓存使用效果更好
    1对1视频聊天源码,优化后的缓存使用效果更好缓存是提升1对1视频聊天源码的有效方法之一,尤其是用户受限于网速的情况下,可以提升系统的响应能力,降低网络的消耗。当然,内容越接近于用户,则缓存的速度就会越快,缓存的有效性则会越高。不过,在1对1视频聊天源码的某些特定场景下缓存还需......
  • 抖音信息流广告如何投效果更好
    随着抖音的迅速崛起,越来越多的广告主选择在抖音平台上投放信息流广告。信息流广告以其精准的定向、高效的转化和较低的成本,成为广告主的优选。那么,如何让抖音信息流广告投放效果更好呢?本文将为你详细解析抖音信息流广告的投放策略,帮助你提升广告效果。一、明确投放目标在......
  • 业余投资者比专业投资者业绩更好
    《战胜华尔街》第一章的标题就是《业余投资比专业投资者业绩更好》,不知道大家是不是对此感到意外?我是比较意外的,虽然我算个个人投资者,坚定的自己买股票,拒绝买基金。但我还是觉得术业有专攻,正真的专业投资者应该比我厉害,只是我不知道哪个基金的背后站着真正的专业投资者,真正的以......