longAdder类

2023-11-12

面试连环炮

先来一连炮简单的面试,看你能顶住几轮?


栈长:

1、多线程情况下,进行数字累加(count++)要注意什么?

张三:

要注意给累加方法加同步锁,不然会出现变量可见性问题,变量值被其他线程覆盖出现不一致的情况

栈长:

2、保证变量可见性,用 volatile 修饰不就行了吗?

张三:

volatile 是可以保证可见性,但不能保证原子性和线程安全

栈长:

3、除了加同步锁这种方案,还有别的方法吗?

张三:

还可以用 JDK 中的原子类,比如:AtomicInteger、AtomicLong,它们是通过 CAS 算法实现的一种乐观锁

栈长:

4、不错,还知道别的么?

张三:

呃……


认真的,你能顶住几轮?

这些问题是 Java 程序员面试过程中必问的,出场率贼高,Java 程序员必懂,这些题在Java面试库小程序中也都有详细答案,这里就不展开了。

你还知道别的么?最后一轮的答案就是今天的主题!

更好的选择:LongAdder

你还在用 AtomicInteger、AtomicLong 原子类进行并发累加操作吗?那你就 OUT 了!

除了 AtomicInteger、AtomicLong,其实在 JDK 8 中更建议使用 LongAdder 进行原子性操作,性能更好,如果你使用的还是 JDK 7-,那当我没说,即使如此,也不能找借口不知道,毕竟 JDK 8 是现在的主流应用版本了。

阿里巴巴最新的 Java开发手册 是这么定义的:

这份阿里巴巴完整的 Java 开发手册,可以关注公众号:Java核心技术,回复:手册,即可下载高清完整版。

如果你还没有用过 LongAdder,不妨看看本文,刷新你的认知,栈长带你涨知识!

为什么搞出了 LongAdder?

我们都知道在 JDK 5 中搞出了 AtomicInteger、AtomicLong 等原子类,这也是在 JDK 8 之前普遍用的原子性操作类,来看下 AtomicLong 的累加源码:

大家都知道这些原子类都是通过 CAS 算法实现的乐观锁,通过旧值和现有的值不断循环比对,直到比对成功才修改成功结束循环。

这样就会有一个问题,如果并发数很高的话,就会造成过多的没有必要的 “循环”,这势必会影响 CPU 的性能。

所以,JDK 8 又搞出来了一个 LongAdder,也在 atomic 包下:

大家可以看到,在同级包中还有一个 LongAccumulator 类,这个这篇不展开,栈长下次再另开一篇具体分析,关注公众号:Java技术栈,写完我会第一时间进行推送。

LongAdder 为什么性能更好?

来分析下 LongAdder 类的源码:

累加

在 LongAdder 中维护了一个 Cell 数组,当 Cell 它不为空时,size 是 2 的次幂大小,每个 Cell 数组里面都有一个初始值为 0 的 long 变量,用来存储每个 Cell 的值:

Cell 类源码

然后其中的 sum 方法用来对 Cell 数组进行求和再加上 base 基础值进行返回:

求和

关于 base 基础值:

LongAdder 并不会一开始就创建 Cell 数组,其本身也会维护一个 base 基础值,当 CAS 更新失败时才进行创建或者扩容。

来看下 AtomicXXX 和 LongAdder 更新对比图:

来源:https://acet.pe.kr/809

Cell 数组相当于一个分段的概念,把 AtomicXXX 中的一个值分成了多个值进行管理,当 CAS 更新失败时不再当前循环重试,而是尝试获取其他的资源锁,这样就降低了对于 AtomicXXX 中的单个资源的竞争,所以 LongAdder 的性能更高。

虽然 LongAdder 性能更好,那有没有缺点呢?

LongAdder 带来了良好的性能,代价肯定也是有的,既然维护了 Cell 数组,也就意味着要占用更多的内存空间,以空间换时间,也是值得的。

实战测试

既然官方都说在高并发的情况下性能更好,事实是否如此呢?

栈长必须实战测试一翻,打消大家的疑虑!

AtomicLong 测试代码:

/**    * @author: 栈长    * @from: 公众号Java技术栈    */   private static void atomicLongTest() throws InterruptedException {       long start = System.currentTimeMillis();       ExecutorService es = Executors.newFixedThreadPool(MAX_POOL_SIZE);       for (int i = 0; i < MAX_POOL_SIZE; i++) {           es.execute(() -> {               for (int j = 0; j < MAX_LOOP_SIZE; j++) {                   atomicLong.incrementAndGet();               }           });       }       es.shutdown();       es.awaitTermination(5, TimeUnit.MINUTES);       System.out.printf("AtomicLong %s*%s 结果:%s,耗时:%sms.\n",               MAX_POOL_SIZE,               MAX_LOOP_SIZE,               atomicLong.get(),               (System.currentTimeMillis() - start));   }   

LongAdder 测试代码:

/**    * @author: 栈长    * @from: 公众号Java技术栈    */   private static void longAdderTest() throws InterruptedException {       long start = System.currentTimeMillis();       ExecutorService es = Executors.newFixedThreadPool(MAX_POOL_SIZE);       for (int i = 0; i < MAX_POOL_SIZE; i++) {           es.execute(() -> {               for (int j = 0; j < MAX_LOOP_SIZE; j++) {                   longAdder.increment();               }           });       }       es.shutdown();       es.awaitTermination(5, TimeUnit.MINUTES);       System.out.printf("LongAdder %s*%s 结果:%s,耗时:%sms.\n",               MAX_POOL_SIZE,               MAX_LOOP_SIZE,               longAdder.sum(),               (System.currentTimeMillis() - start));   }   

这里只贴核心测试代码了,完整代码已上传到了 Github:

https://github.com/javastacks/javastack

测试结果:

这里测试的只有是 1 个线程,每个线程循环累加 1 次,这个没有锁竞争、没有高并发操作的场景就能看出性能上的差异了。。

栈长再不断提升 线程数、循环累加次数 ,得到了以下测试结果:

线程数 * 循环次数 AtomicLong LongAdder
1 * 1 45ms 1ms
10 * 10 55ms 2ms
10 * 100 56ms 2ms
100 * 10 58ms 10ms
100 * 100 74ms 10ms
1000 * 10 190ms 71ms
1000 * 100 217ms 73ms
1000 * 1000 194ms 81ms
1000 * 10000 301ms 114ms
1000 * 100000 1813ms 277ms
1000 * 1000000 17596ms 1629ms

图表对比:

从测试结果可以看出,LongAdder 的性能都是碾压 AtomicLong 的,最高可达 28 多倍的差距(56/2),可以说在高性能要求的高并发场景,肯定是有必要用 LongAdder 的,这也是阿里巴巴为什么建议使用 LongAdder 的原因。

当然,这只是我个人的测试,这个也和硬件配置有关系的,但毋庸置疑是,AtomicLong 的性能是更好的。另外,栈长写的 Java 多线程系列教程全部整理好了,微信搜索Java技术栈,在后台发送:Java,可以在线阅读。

总结

本文以一场面试连环炮揭开了 LongAdder 的面纱,怎么解决 count++ 的线程安全性问题?

栈长再总结下:

  • 累加方法加 synchronized/ Lock 同步锁;

  • 使用 AtomicInteger/ AtomicLong 原子类;

  • 使用 LongAdder 原子类(推荐使用);

LongAdder 这个东西是 Java 8 搞出来的,用来代替 AtomicXXX,不管是否高并发场景,都完胜 AtomicXXX,它不仅可以改善性能,现在面试也问的越来越多了,大家还是有必要掌握。

虽然 LongAdder 性能更好,但也是以更多的内存空间消耗为代价的,当然,现代计算机,内存早已不是瓶颈,所以这点消耗是可以忽略不计的,性能还是最重要的,但是大家也要知道这个点。

本文转自 https://mp.weixin.qq.com/s/ND0a6qm98QtuIWNYgF7U3A,如有侵权,请联系删除。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

longAdder类 的相关文章

随机推荐

  • 【牛客网华为机试】HJ32 密码截取

    题目 描述 Catcher是MCA国的情报员 他工作时发现敌国会用一些对称的密码进行通信 比如像这些ABBA ABA A 123321 但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解 比如进行下列变化 ABBA gt 12AB
  • css怎样去除表格内边距,CSS语言border-spacing表格边距控制

    在前面我看讲过表格的边框分离与合并 现在就给大家讲下表格分离后如何用CSS语言中的border spacing属性控制表格间的宽度 语法 border spacing px px 说明 border spacing第一个是水平间隔 第二个是
  • C++内存布局和内存分配

    看了很多 收集若干篇 内存布局1 内存布局2 内存分配 内存布局图解析 mmap内存映射 brk内存分配原理 拓展 从内核文件系统看文件的读写过程 栈溢出 一般都是临时变量给的太大超过8MB
  • 初识c语言(上)

    目录 前言 什么是C语言 第一个C语言代码 通用类型 如何定义变量与常量 结束 前言 本篇只对C语言做基础的介绍 对C语言基础有个大概的了解 一 什么是c语言 C语言是门语言 就像人与人之间交流用汉语 英语 法语等等 而C语言是实现人机交流
  • PHP发送邮件(PHPMailer)

    作为PHP入门开发者 常常有这种述求 自己的网站中需要添加一个使用自己的域名作为发件人邮件地址的自动发送邮件的方法 用于诸如给用户发送验证码 通知信息等 比如 我的某个用户注册模块 需要使用register jjonline cn作为邮件发
  • 你真的了解 Synchronized 吗?

    前言 Synchronized想必大家在工作中一定有接触过 它算是Java并发场景下实现多线程安全一种比较直接的操作 有人会说它慢 确实 在JDK1 6之前 它有另一个名称叫做 重量级锁 但是从1 6版本起 它就在不断被优化 现如今已经是很
  • /usr/bin/ld: libx264.a(dct-a.o): relocation R_X86_64_32 against `.rodata‘ can not be used when makin

    最近在编译x264的时候出现了错误 usr bin ld libx264 a dct a o relocation R X86 64 32 against rodata can not be used when making a PIE o
  • package.json详解

    在package json中配置环境变量 scripts dev cross env NODE ENV development platform h5 webpack mode development config webpack conf
  • 【华为诺亚方舟实验室】2022届毕业生招聘--决策(强化学习)推理方向

    深度强化学习实验室 官网 http www neurondance com 论坛 http deeprl neurondance com 来源 华为诺亚方舟实验室官微 诺亚方舟实验室 Noah s Ark Lab 是华为公司从事人工智能基础
  • Redis系列之基础知识

    文章目录 一 Redis是什么 Redis架构 二 为什么使用Redis 三 Redis支持的数据类型 四 Redis支持的特殊数据结构 五 Redis持久化 RDB Redis DataBase AOF Append Only File
  • C++ 实验五 NO.1 定义一个student类包含成员变量姓名,年龄,学号,用多文件组织类, 在student类中重载构造函数,用多种方式对该类的对象初始化

    文章目录 题目 main cpp student cpp student h course cpp course h 运行结果 题目 程序作者 冰糖 章 节 实验五 最后修改日期 2019年3月31日 题 目 实验目的 1 掌握类的定义 根
  • 微信错误码:"errcode":40003 解决方法汇总

    一 问题描述 最近公司在搞微信小程序开发 然后自己负责一个小程序的模板消息发送 调用其中的 https api weixin qq com cgi bin message wxopen template send access token
  • 静态链接与动态链接的区别

    我们大家在编程过程中对 链接 这个词并不陌生 链接所解决的问题即是将我们自己写的代码和别人写的库集成在一起 链接可以分为静态链接与动态链接 下文将分别讲解这两种方式的特点与其区别 静态链接 特点 在生成可执行文件的时候 链接阶段 把所有需要
  • 学习Java虚拟机的正确的姿势?

    为什么要学习Java虚拟机 多线程和虚拟机 实际工作中 大部分程序员可能几乎不用 但这两项技能是你面试所谓高级工程师的敲门砖 也是你在机会到来的时候能否顶上去的弹药库 很多人 把这两部看的太高深 望而却步 我觉得一个重要原因就是大部分博客和
  • Unity通过Dll热更新

    Demo资源 1 需要创建一个 NET Framework 3 5 类库 2 在解决方案资源管理器中添加引用Unity3个Dll文件 UnityEngine UnityEngine CoreModule UnityEngine UI 3 在
  • hdu 1827 Summer Holiday (强连通分量)

    Summer Holiday Time Limit 10000 1000 MS Java Others Memory Limit 32768 32768 K Java Others Total Submission s 1626 Accep
  • 强类型枚举以及C++11对原有枚举类型的扩展

    强枚举类型 非强类型作用域 允许隐式转换为整型 占用存储空间及符号性不确定 都是枚举类的缺点 针对这些缺点 新标准C 11引入了一种新的枚举类型 即 枚举类 又称 强类型枚举 语法 在enum后加上关键字class enum class E
  • Naive Bayes 朴素贝叶斯的JAVA代码实现

    工程文件可在http download csdn net detail u011321908 6385295下载 下面贴的代码仅是主类程序 1 关于贝叶斯分类 bayes 是一种统计学分类方法 它基于贝叶斯定理 它假定一个属性值对给定类的影
  • 使用国内npm安装electron

    由于国内墙的问题 使用npm下载electron绝大多数情况下会报超时 使用yarn安装直接报失败 在此记录一下尝试成功的下载步骤 使用淘宝镜像 执行的前置命令 npm config set registry https registry
  • longAdder类

    面试连环炮 先来一连炮简单的面试 看你能顶住几轮 栈长 1 多线程情况下 进行数字累加 count 要注意什么 张三 要注意给累加方法加同步锁 不然会出现变量可见性问题 变量值被其他线程覆盖出现不一致的情况 栈长 2 保证变量可见性 用 v