java 并发包之 LongAdder 源码分析
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。
LongAdder是java8中新增的原子类,在多线程环境中,它比AtomicLong性能要高出不少,特别是写多的场景。
它是怎么实现的呢?让我们一起来学习吧。
原理
LongAdder的原理是,在最初无竞争时,只更新base的值,当有多线程竞争时通过分段的思想,让不同的线程更新不同的段,最后把这些段相加就得到了完整的LongAdder存储的值。
源码分析
LongAdder继承自Striped64抽象类,Striped64中定义了Cell内部类和各重要属性。
主要内部类
Cell类使用@sun.misc.Contended注解,说明是要避免伪共享的。
使用Unsafe的CAS更新value的值,其中value的值使用volatile修饰,保证可见性。
关于Unsafe的介绍请查看【死磕 java魔法类之Unsafe解析】。
关于伪共享的介绍请查看【杂谈 什么是伪共享(false sharing)?】。
主要属性
最初无竞争或有其它线程在创建cells数组时使用base更新值,有过竞争时使用cells更新值。
最初无竞争是指一开始没有线程之间的竞争,但也有可能是多线程在操作,只是这些线程没有同时去更新base的值。
有过竞争是指只要出现过竞争不管后面有没有竞争都使用cells更新值,规则是不同的线程hash到不同的cell上去更新,减少竞争。
add(x)方法
add(x)方法是LongAdder的主要方法,使用它可以使LongAdder中存储的值增加x,x可为正可为负。
(1)最初无竞争时只更新base;
(2)直到更新base失败时,创建cells数组;
(3)当多个线程竞争同一个Cell比较激烈时,可能要扩容;
sum()方法
sum()方法
可以看到sum()方法是把base和所有段的值相加得到,那么,这里有一个问题,如果前面已经累加到sum上的Cell的value有修改,不是就没法计算到了么?
答案确实如此,所以LongAdder可以说不是强一致性的,它是最终一致性的。
LongAdder VS AtomicLong
当只有一个线程的时候,AtomicLong反而性能更高,随着线程越来越多,AtomicLong的性能急剧下降,而LongAdder的性能影响很小。
总结
(1)LongAdder通过base和cells数组来存储值;
(2)不同的线程会hash到不同的cell上去更新,减少了竞争;
(3)LongAdder的性能非常高,最终会达到一种无竞争的状态;
彩蛋
在longAccumulate()方法中有个条件是 n>=NCPU
就不会走到扩容逻辑了,而n是2的倍数,那是不是代表cells数组最大只能达到大于等于NCPU的最小2次方?
答案是明确的。因为同一个CPU核心同时只会运行一个线程,而更新失败了说明有两个不同的核心更新了同一个Cell,这时会重新设置更新失败的那个线程的probe值,这样下一次它所在的Cell很大概率会发生改变,如果运行的时间足够长,最终会出现同一个核心的所有线程都会hash到同一个Cell(大概率,但不一定全在一个Cell上)上去更新,所以,这里cells数组中长度并不需要太长,达到CPU核心数足够了。
比如,笔者的电脑是8核的,所以这里cells的数组最大只会到8,达到8就不会扩容了。
java 并发包之 LongAdder 源码分析相关推荐
- 死磕java并发cas_死磕 java并发包之AtomicInteger源码分析
问题 (1)什么是原子操作? (2)原子操作和数据库的ACID有啥关系? (3)AtomicInteger是怎么实现原子操作的? (4)AtomicInteger是有什么缺点? 简介 AtomicIn ...
- 【java】LongAdder源码分析原理分析
文章目录 1.概述 1.1 为何要引入LongAdder? 2.LongAdder案例 3.LongAdder源码分析 3.1 设计思路 3.2 数据对比 3.3 add 3.3.1 longAccu ...
- Java并发编程之ThreadLocal源码分析
1 一句话概括ThreadLocal 什么是ThreadLocal?顾名思义:线程本地变量,它为每个使用该对象的线程创建了一个独立的变量副本. 2 ThreadLocal使用场景 用一句话总结 ...
- Java集合篇:LinkedList源码分析
(注:本文内容基于JDK1.6) 一.概述: LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口 ...
- java多线程系列:ThreadPoolExecutor源码分析
前言 这篇主要讲述ThreadPoolExecutor的源码分析,贯穿类的创建.任务的添加到线程池的关闭整个流程,让你知其然所以然.希望你可以通过本篇博文知道ThreadPoolExecutor是怎么 ...
- java中Mark接口_JVM源码分析之Java对象头实现
原标题:JVM源码分析之Java对象头实现 原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 "365篇原创计划"第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Ja ...
- java多线程系列:ThreadPoolExecutor源码分析,java基础面试笔试题
我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...
- Java设计模式学习以及底层源码分析
源码在分支master 工厂模式 把具体创建产品的细节封装起来,你要什么产品,我给你什么产品即可. 简单工厂模式 工厂方法模式 缓存层:抽象类 抽象工厂模式 缓存层是:接口 原型模式 问题: 原型模式 ...
- java中jcl,spring-jcl 日志源码分析
1.spring-jcl介绍 JCL全称:Jakarta Commons Logging spring-jcl 采用了设计模式中的"适配器模式",它对外提供统一的接口,然后在适配类 ...
最新文章
- Zenoss Announces Monitoring for VMWare's Cloud Director
- spring下redis开发环境搭建
- 第四讲 deque
- 计算机网络-思维导图(1)概述
- CSS之Multi-columns的列数和列宽
- 【转载】飞鸽传书2013官方下载
- 服务器防火墙,linux下iptables防火墙配置相关
- 电影票房数据查询服务高性能与高可用实践
- php 统计页面跳失率,究竟网店各页面的跳失率大小为多少才算正常水平?
- 关于对QQ 输入法的评价
- Shell 获取进程号
- java线程系列一:Thread类中的start()方法与run方法
- TensorBoard 使用案例
- 69. Sqrt(x)
- python输入年份判断生肖_python年份判断生肖
- 从招聘网站分析大数据相关职位现状
- 联想G40重装linux系统,联想G40笔记本重装XP系统教程
- 小象学院python数据分析课程怎么样_数据分析和数据挖掘-2016小象学院
- 双基因突变患者_宁夏发现世界首例双基因突变病例 患者矮小
- docker中安装nacos报错 com.alibaba.nacos.shaded.io.grpc.StatusRuntimeException: UNAVAILABLE: io exception
热门文章
- 多线程知识梳理(1) - 并发编程的艺术笔记
- ros amcl 参数配置
- html5设置视频显示第一帧,如何检测HTML5视频何时播放第一帧?
- 【PAT - 甲级1155】Heap Paths (30分)(栈,dfs,二叉树)
- oracle 1天后,Oracle Code One - 第1天 精彩亮点回顾
- 清楚linux缓存文件,Linux删除文件 清除缓存
- java用链表做学生系统_C语言链表实现学生管理系统
- Linux生态ox版本,从折腾说Linux生态圈
- python websocket服务器https_Socket与WebSocket以及http与https重新总结
- Android 入门(四) | Intent 实现 Activity 切换