点击关注公众号,实用技术文章及时了解

来源:blog.csdn.net/limenghua9112/

article/details/107950744

概述

AtomicLong是作者Doug Lea在jdk1.5版本发布于java.util.concurrent.atomic并发包下的类。而LongAdder是道格·利(Doug Lea的中文名)在java8中发布的类。

有了AtomicLong为何还需要LongAdder?

在这里,就不得不分析一下AtomicLong的缺点了。

先来看一下AtomicLong.incrementAndGet()方法的源码

/*** Atomically increments by one the current value.** @return the updated value*/
public final long incrementAndGet() {return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

接着跟踪Unsafe类的getAndAddLong方法

图:Unsafe类的getAndAddLong方法

可以清楚地看到,AtomicLong的原子性自增操作,是通过CAS实现的。

在多线程竞争不激烈的情况下,这样做是合适的。但是如果线程竞争激烈,会造成大量线程在原地打转、不停尝试去修改值,但是老是发现值被修改了,于是继续自旋。这样浪费了大量的CPU资源。

而且,由于AtomicLong持有的成员变量value是volatile关键字修饰的,线程修改了临界资源后,需要刷新到其他线程,也是要费一番功夫的。

画个图来理解:

图:volatile刷新共享内存。

而LongAdder也有一个volatile修饰的base值,但是当竞争激烈时,多个线程并不会一直自旋来修改这个值,而是采用了分段的思想。竞争激烈时,各个线程会分散累加到自己所对应的Cell[]数组的某一个数组对象元素中,而不会大家共用一个。

这样做,可以把不同线程对应到不同的Cell中进行修改,降低了对临界资源的竞争。本质上,是用空间换时间。

LongAdder 和 AtomicLong 的性能对比

分析了半天,没有证据,还是不能让人信服的。唯有证明一下,才能心服口服。

接下来,我会创建一个容量为1,000的固定线程池,然后提交100倍于线程池容量的线程,每个线程中,对临界资源进行+1操作。等所有线程执行结束后,统计运行时长,并关闭线程池。临界资源分别使用AtomicLong和LongAdder表示,来对比二者的区别。

首先尝试让每个线程进行100次+1操作,最后的累加结果应该为:线程数×100=1,000×100×100 = 10,000,000

AtomicLongDemo.java

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;/*** <pre>* 程序目的:演示 AtomicInteger、AtomicLong 在高并发下性能不好* 在16个线程下使用AtomicLong。* 每次值发生变化时,都会刷新回主内存,竞争激烈时,这样的 flush 和 refresh 操作耗费了很多资源,而且 CAS 也会经常失败* </pre>* created at 2020/8/11 06:11* @author lerry*/
public class AtomicLongDemo {/*** 线程池内线程数*/final static int POOL_SIZE = 1000;public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();AtomicLong counter = new AtomicLong(0);ExecutorService service = Executors.newFixedThreadPool(POOL_SIZE);ArrayList<Future> futures = new ArrayList<>(POOL_SIZE);for (int i = 0; i < POOL_SIZE * 100; i++) {futures.add(service.submit(new Task(counter)));}// 等待所有线程执行完for (Future future : futures) {try {future.get();}catch (ExecutionException e) {e.printStackTrace();}}NumberFormat numberFormat = NumberFormat.getInstance();System.out.printf("统计结果为:[%s]\n", numberFormat.format(counter.get()));System.out.printf("耗时:[%d]毫秒", (System.currentTimeMillis() - start));// 关闭线程池service.shutdown();}/*** 有一个 AtomicLong 成员变量,每次执行N次+1操作*/static class Task implements Runnable {private final AtomicLong counter;public Task(AtomicLong counter) {this.counter = counter;}/*** 每个线程执行N次+1操作*/@Overridepublic void run() {for (int i = 0; i < 100; i++) {counter.incrementAndGet();}}// end run}// end class
}

LongAdderDemo.java

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.LongAdder;/*** <pre>* 程序目的:和 AtomicLong 进行性能对比* </pre>* created at 2020/8/11 06:25* @author lerry*/
public class LongAdderDemo {/*** 线程池内线程数*/final static int POOL_SIZE = 1000;public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();LongAdder counter = new LongAdder();ExecutorService service = Executors.newFixedThreadPool(POOL_SIZE);ArrayList<Future> futures = new ArrayList<>(POOL_SIZE);for (int i = 0; i < POOL_SIZE * 100; i++) {futures.add(service.submit(new LongAdderDemo.Task(counter)));}// 等待所有线程执行完for (Future future : futures) {try {future.get();}catch (ExecutionException e) {e.printStackTrace();}}NumberFormat numberFormat = NumberFormat.getInstance();System.out.printf("统计结果为:[%s]\n", numberFormat.format(counter.sum()));System.out.printf("耗时:[%d]毫秒", (System.currentTimeMillis() - start));// 关闭线程池service.shutdown();}/*** 有一个 LongAdder 成员变量,每次执行N次+1操作*/static class Task implements Runnable {private final LongAdder counter;public Task(LongAdder counter) {this.counter = counter;}/*** 每个线程执行N次+1操作*/@Overridepublic void run() {for (int i = 0; i < 100; i++) {counter.increment();}}// end run}// end class
}

备注:AtomicLong的运行结果截图和LongAdder的运行结果截图放在了一起,AtomicLong的在上、LongAdder的在下。另外,关注Java知音公众号,回复“后端面试”,送你一份面试题宝典!

每个线程进行100次累加的运行结果

图:100次累加的执行结果

可以看到,AtomicLong耗时516毫秒,LongAdder耗时438毫秒,516➗438≈1.18,前者耗时是后者的一倍多一点。区别好像不是很大。

进行1,000次累加呢?

图:1000次累加的执行结果

可以看到,AtomicLong耗时3034毫秒,LongAdder耗时575毫秒,3034➗575≈5.28,前者耗时是后者的5倍多。区别开始变得明显。

进行10,000次累加呢?

图:10,000次累加的执行结果

可以看到,AtomicLong耗时30868毫秒,LongAdder耗时2167毫秒,30868➗2167≈14.24,前者耗时是后者的14倍多。差距变得更大了。

进行50,000次累加呢?

图:50,000次累加的执行结果

可以看到,AtomicLong耗时148375毫秒,LongAdder耗时9754毫秒,148375➗9754≈15.21,前者耗时是后者的15倍多。差距进一步扩大。

结论

在每个线程执行的累加数量变多时,LongAdder比AtomicLong性能优势越发明显。

LongAdder由于采用了分段理念,降低了线程间的竞争冲突,而AtomicLong却因多个线程并行竞争同一个value值,从而影响了性能。

在低竞争的情况下,AtomicLong 和 LongAdder 这两个类具有相似的特征,吞吐量也是相似的,因为竞争不高。

但是在竞争激烈的情况下,LongAdder 的预期吞吐量要高得多,经过试验,LongAdder 的吞吐量大约是 AtomicLong 的十倍,不过凡事总要付出代价,LongAdder 在保证高效的同时,也需要消耗更多的空间。

环境说明

  • java -version

java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)
  • OS:macOS High Sierra 10.13.4

参考资料

  • https://xilidou.com/2018/11/27/LongAdder/

●【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能

●分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!

●能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

LongAdder和AtomicLong哪个性能更好,为什么?相关推荐

  1. 【078期】java.util.* 并发包下 LongAdder 和 AtomicLong 哪个性能更好,为什么?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方留言必回,有问必答! 每天 08:00 更新文章,每天进步一点点... 概述 ...

  2. 性能优化之使用LongAdder替换AtomicLong

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:https://juejin.cn/post/6921595303460241415 写在前面 本篇文章并不会直接进入主题讲为 ...

  3. [JDK8]性能优化之使用LongAdder替换AtomicLong

    如果让你实现一个计数器,有点经验的同学可以很快的想到使用AtomicInteger或者AtomicLong进行简单的封装. 因为计数器操作涉及到内存的可见性和线程之间的竞争,而Atomic***的实现 ...

  4. LongAdder和AtomicLong

    LongAdder AmoticLong AtomicLong是作用是对长整形进行原子操作. 原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束. 在32位操作系统中,64位 ...

  5. Java中的LongAdder和AtomicLong有什么区别?

    ● Java中的LongAdder和AtomicLong有什么区别? 考点:JDK 参考回答: JDK1.8引入了LongAdder类.CAS机制就是,在一个死循环内,不断尝试修改目标值,直到修改成功 ...

  6. UC 伯克利华人一作:卷积让视觉 Transformer 性能更强,ImageNet 继续刷点!

    转自 | 新智元 来源 | arXiv 编辑 | LQ Priscilla Convolutional stem is all you need! Facebook AI和UC伯克利联手,探究视觉Tr ...

  7. 谷歌实现2种新的强化学习算法,“比肩”DQN,泛化性能更佳!|ICLR 2021

    丰色 发自 凹非寺 量子位 报道 | 公众号 QbitAI 强化学习(RL)算法持续"进化"中-- 来自Google Research的研究人员,证明可以使用图表示 (graph ...

  8. IntelliJ IDEA 2019.2最新解读:性能更好,体验更优,细节处理更完美!

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! IntelliJ IDEA 2019.2 准备 idea 2019.2正式版是在2019年7 ...

  9. HRFormer 多分辨率Transformer 参数骤降,性能更强

    今天推荐的文章是来自中科院.北大.微软亚研.百度联手提出HRFormer模型,能够在多分辨率并行,参数量下降了40%,但是性能更好!本文也被收录NeurIPS 2021,文章2021年10月份刚出来 ...

最新文章

  1. R语言dplyr包使用select函数通过索引查询或者排除数据列实战(Select Columns by Index)
  2. c++ 自定义比较函数,运行时发生segmentation fault
  3. A Windows Mobile GPS Application Sample – Using Fake GPS
  4. Ubuntu 14.04 安装flash插件;安装Cairo-Dock; 美化为Mac
  5. iView 实现可编辑表格 1
  6. DevOps实践教程 华为云 系列教程2021 合集
  7. 微信小程序生成二维码接口调用
  8. 漫谈软件系统测试——缺陷分析
  9. 201510月职称计算机题目,职称计算机考试模拟题库-Excel试题及答案11
  10. 四部金融投资经典教材和30部必读的投资学经
  11. walking机器人仿真教程-应用-多点导航结合睡眠功能实现智能取药
  12. windows操作系统杂谈
  13. EDG:相信奇迹的人,本身就是奇迹。
  14. 计算机专业毕业论文选哪方面,计算机专业毕业论文格式有哪些要求
  15. 微软2019 Windows 10更新五月版:10个不可错过的新功能
  16. React中文文档 8. 列表 Key
  17. 转帖:夜夜听雨(四)(中国社会科学院 郑哲)
  18. 数据库SQL实战-查找描述信息中包括robot的电影对应的分类名称以及电影数目(mysql)
  19. SpringBoot--访问静态页面
  20. 华为手机鸿蒙系统手机_升级快讯:又一批华为手机可以升级到“鸿蒙系统”了!...

热门文章

  1. 最便宜的855旗舰了,可惜是它
  2. 没想到熬夜看了场重播!iPhone 11系列正式发布:连价格都被猜到了
  3. 马云卸任浙江阿里小额贷款股份有限公司法定代表人、董事长
  4. 疑似一加7 Pro 5G版通过3C认证:配备30W快充头
  5. 四款855旗舰对比:除开价格,各有优点
  6. 抢注“哔哩哔哩”商标卖成人用品?A站回应:不符合价值观 已申请注销
  7. 防火等级不达标!索尼宣布召回中国大陆地区部分充电宝 可免费换新
  8. 华为P30 Pro终极渲染图曝光:后置徕卡四摄颜值出众
  9. MyBatis-Plus自动填充功能填坑
  10. Redis深入浅出----演讲