请选择 进入手机版 | 继续访问电脑版
搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

原子类型累加器

[复制链接]
查看: 63|回复: 0

2万

主题

3万

帖子

8万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
81369
发表于 2020-1-14 14:43 | 显示全部楼层 |阅读模式
本博客系列是进修并发编程进程中的记载总结。由于文章比力多,写的时候也比力散,所以我整理了个目录贴(传送门),方便查阅。
并发编程系列博客传送门
原子典范累加器JDK1.8引进的并发新技术,它可以看做AtomicLongAtomicDouble的部分增强典范。
原子典范累加器有以下四种:

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder
由于上面四种累加器的道理类似,下面以LongAdder为列来先容累加器的操纵。以下内存是转载内容,原文请见此博客
LongAdder简介

JDK1.8时,java.util.concurrent.atomic包中供给了一个新的原子类:LongAdder。
按照Oracle官方文档的先容,LongAdder在高并发的场景下会比它的先辈————AtomicLong 具有更好的性能,价格是消耗更多的内存空间:
原子类型累加器  游戏

那末,题目来了:
为什么要引入LongAdder? AtomicLong在高并发的场景下有什么题目吗? 假如低并发情况下,LongAdder和AtomicLong性能差不多,那LongAdder能否便可以替换AtomicLong了?
为什么要引入LongAdder?

我们晓得,AtomicLong是操纵了底层的CAS操纵来供给并发性的,比如addAndGet方式:
原子类型累加器  游戏

上述方式挪用了Unsafe类的getAndAddLong方式,该方式是个native方式,它的逻辑是采纳自旋的方式不停更新方针值,直到更新乐成。
在并发量较低的情况下,线程辩说的几率比力小,自旋的次数不会很多。可是,高并发情况下,N个线程同时举行自旋操纵,会出现大量失利并不停自旋的情况,此时AtomicLong的自旋会成为瓶颈。
这就是LongAdder引入的初衷——治理高并发情况下AtomicLong的自旋瓶颈题目。
LongAdder快在何处?

既然说到LongAdder可以明显提升高并发情况下的性能,那末它是怎样做到的?这里先简单的说下LongAdder的思绪,第二部分会详述LongAdder的道理。
我们晓得,AtomicLong中有个内部变量value保存着现实的long值,全数的操纵都是针对该变量举行。也就是说,高并发情况下,value变量实在是一个热门,也就是N个线程合作一个热门。
LongAdder的底子思绪就是分离热门,将value值分离到一个数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值举行CAS操纵,这样热门就被分离了,辩说的几率就小很多。假如要获得实在的long值,只要将各个槽中的变量值累加返回。
这类做法有没有似曾了解的感受?没错,ConcurrentHashMap中的“分段锁”实在就是类似的思绪。
LongAdder能否替换AtomicLong?

回答这个题目之前,我们先来看下LongAdder供给的API:
原子类型累加器  游戏

可以看到,LongAdder供给的API和AtomicLong比力靠近,两者都能以原子的方式对long型变量举行增减。
可是AtomicLong供给的功用实在更丰富,特别是addAndGetdecrementAndGetcompareAndSet这些方式。
addAndGetdecrementAndGet除了纯真的做自增自减外,还可以立即获得增减后的值,而LongAdder则需要做同步控制才华正确获得增减后的值。假如营业需求需要正确的控制计数,做计数比力,AtomicLong也更合适。
此外,从空间方面考虑,LongAdder实在是一种“空间换时候”的脑筋,从这一点来说AtomicLong更适当。固然,假如你必定要跟我杠今世主机的内存对于这点消耗底子不算什么,那我也法子。
总之,低并发、一样平常的营业场景下AtomicLong是充沛了。假如并发量很多,存在大量写多读少的情况,那LongAdder大要更合适。适当的擦轭好的,假如真出现了需要考虑到底用AtomicLong好还是LongAdder的营业场景,那末这样的会商是没故意义的,由于这类情况下要末举行性能测试,以正确评价在当前营业场景下两者的性能,要末换个思绪追求此外治理计划。
末端,给出国外一位博主对LongAdder和AtomicLong的性能评测,以供参考:http://blog.palominolabs.com/...
LongAdder道理

之前说了,AtomicLong是多个线程针对单个热门值value举行原子操纵。而LongAdder是每个线程具有自己的槽,各个线程一样平常只对自己槽中的那个值举行CAS操纵。
比若有三个ThreadA、ThreadB、ThreadC,每个线程对value增加10。
对于AtomicLong,最终成果的盘算始终是下面这个形式:
可是对于LongAdder来说,内部有一个base变量,一个Cell[]数组。
base变量:非竞态条件下,间接累加到该变量上
Cell[]数组:竞态条件下,累加个各个线程自己的槽Cell
最终成果的盘算是下面这个形式:
LongAdder的内部结构

LongAdder只要一个空机关器,其自己也没有什么特此外地方,全数复杂的逻辑都在它的父类Striped64中。
原子类型累加器  游戏

来看下Striped64的内部结构,这个类实现一些焦点操纵,处置惩罚64位数据。
Striped64只要一个空机关器,初始化时,经过Unsafe获得到类字段的偏移量,以便后续CAS操纵:
原子类型累加器  游戏

上面有个比力特此外字段是threadLocalRandomProbe,可以把它看成是线程的hash值。这个后背我们会讲到。
界说了一个内部Cell类,这就是我们之前所说的槽,每个Cell工具存有一个value值,可以经过Unsafe来CAS操纵它的值:
原子类型累加器  游戏

此外的字段:
可以看到Cell[]就是之条件到的槽数组,base就黑白并发条件下的基数累计值。
原子类型累加器  游戏

LongAdder的焦点方式

还是经过例子来看:
假定现在有一个LongAdder工具la,四个线程A、B、C、D同时对la举行累加操纵。
  1. LongAdder la = new LongAdder();la.add(10);
复制代码
ThreadA挪用add方式(假定此时没有并发):
原子类型累加器  游戏

初始时Cell[]为null,base为0。所以ThreadA会挪用casBase方式(界说在Striped64中),由于没有并发,CAS操纵乐成将base变成10:
原子类型累加器  游戏

可以看到,假如线程A、B、C、D线性实行,那casBase永久不会失利,也就永久不会进入到base方式的if块中,全数的值城市积累到base中。
那末,假如尽情线程有并发辩说,致使caseBase失利呢?
失利就会进入if方式体:
原子类型累加器  游戏

这个方式了解先再次判定Cell[]槽数组有没初始化过,假如初始化过了,今后全数的CAS操纵都只针对槽中的Cell;否则,进入longAccumulate方式。
全部add方式的逻辑以下图:
原子类型累加器  游戏

可以看到,只要从未出现过并发辩说的时候,base基数才会操纵到,一旦出现了并发辩说,以后全数的操纵都只针对Cell[]数组中的单元Cell。
假如Cell[]数组未初始化,会挪用父类的longAccumelate去初始化Cell[],假如Cell[]已经初始化可是辩说发生在Cell单元内,则也挪用父类的longAccumelate,此时大要就需要对Cell[]扩容了。
这也是LongAdder计划的精巧之处:尽管淘汰热门辩说,不到末端万不得已,尽管将CAS操纵延长。
Striped64的焦点方式

我们来看下Striped64的焦点方式longAccumulate到底做了什么:
原子类型累加器  游戏

上述代码首先给当前方程分派一个hash值,然落后入一个自旋,这个自旋分为三个分支:

  • CASE1:Cell[]数组已经初始化
  • CASE2:Cell[]数组未初始化
  • CASE3:Cell[]数组正在初始化中
CASE2:Cell[]数组未初始化

我们之前会商了,初始时Cell[]数组还没有初始化,所以会进入分支②:
原子类型累加器  游戏

首先会将cellsBusy置为1-加锁状态
原子类型累加器  游戏

然后,初始化Cell[]数组(初始巨细为2),按照当前方程的hash值盘算映照的索引,并建立对应的Cell工具,Cell单元中的初始值x就是本主要累加的值。
CASE3:Cell[]数组正在初始化中

假如在初始化进程中,另一个线程ThreadB也进入了longAccumulate方式,就会进入分支③:
原子类型累加器  游戏

可以看到,分支③间接操纵base基数,将值累加到base上。
CASE1:Cell[]数组已经初始化

假如初始化完成后,此外线程也进入了longAccumulate方式,就会进入分支①:
原子类型累加器  游戏

全部longAccumulate的流程图以下:
原子类型累加器  游戏

LongAdder的sum方式

末端,我们来看下LongAddersum方式:
原子类型累加器  游戏

sum求和的公式就是我们开首说的:
需要留意的是,这个方式只能获得某个时候的近似值,这也就是LongAdder并不能完全替换LongAtomic的原因原由之一。
LongAdder的此外兄弟

JDK1.8时,java.util.concurrent.atomic包中,除了新引入LongAdder外,还有引入了它的三个兄弟类:LongAccumulatorDoubleAdderDoubleAccumulator
原子类型累加器  游戏

LongAccumulator

LongAccumulatorLongAdder的增强版。LongAdder只能针对数值的举行加减运算,而LongAccumulator供给了自界说的函数操纵。其机关函数以下:
原子类型累加器  游戏

经过LongBinaryOperator,可以自界说对入参的尽情操纵,并返回成果(LongBinaryOperator吸收2个long作为参数,并返回1个long)
LongAccumulator内部道理和LongAdder几乎完全一样,都是操纵了父类Striped64longAccumulate方式。这里就不再赘述了,读者可以自己阅读源码。
DoubleAdder和DoubleAccumulator

从名字也可以看出,DoubleAdderDoubleAccumulator用于操纵double原始典范。
LongAdder的唯一区分就是,其内部会经过一些方式,将原始的double典范,转换为long典范,此外和LongAdder完全一样:
原子类型累加器  游戏


免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2006-2014 妈妈网-中国妈妈第一,是怀孕、育儿、健康等知识交流传播首选平台 版权所有 法律顾问:高律师 客服电话:0791-88289918
技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表