仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 496|回复: 9
打印 上一主题 下一主题

[学习教程] JAVA教程之比AtomicLong还高效的LongAdder 源码剖析仓酷云

[复制链接]
飘飘悠悠 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:19:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

x
C#是不行的,比如说美国的航天飞船里就有java开发的程序以上是我的愚见,其实不管那种语言,你学好了,都能找到好的工作,打仗到AtomicLong的缘故原由是在看guava的LoadingCache相干代码时,关于LoadingCache,实在思绪也十分复杂明晰:用模板形式办理了缓存不射中时猎取数据的逻辑,这个思绪我早前也恰好在项目中利用到。
言回正传,为何说LongAdder引发了我的注重,缘故原由有二:
1.作者是Douglea,位置其实无足轻重。
2.他说这个比AtomicLong高效。
我们晓得,AtomicLong已长短常好的办理计划了,触及并发的中央都是利用CAS操纵,在硬件条理上往做compareandset操纵。效力十分高。
因而,我决意研讨下,为何LongAdder比AtomicLong高效。
起首,看LongAdder的承继树:

承继自Striped64,这个类包装了一些很主要的外部类和操纵。稍候会看到。
正式入手下手前,夸大下,我们晓得,AtomicLong的完成体例是外部有个value变量,当多线程并发自增,自减时,均经由过程cas指令从呆板指令级别操纵包管并发的原子性。
再看看LongAdder的办法:

怪不得能够和AtomicLong作对照,连API都这么像。我们任意挑一个API动手剖析,这个API通了,其他API都迥然不同,因而,我选择了add这个办法。现实上,其他API也都依附这个办法。

LongAdder中包括了一个Cell数组,Cell是Striped64的一个外部类,望文生义,Cell代表了一个最小单位,这个单位有甚么用,稍候会说道。先看界说:

Cell外部有一个十分主要的value变量,而且供应了一个CAS更新其值的办法。
回到add办法:

这里,我有个疑问,AtomicLong已利用CAS指令,十分高效了(比起各类锁),LongAdder假如仍是用CAS指令更新值,怎样大概比AtomicLong高效了?况且外部还这么多判别!!!
这是我入手下手时最年夜的疑问,以是,我料想,岂非有比CAS指令更高效的体例呈现了?带着这个疑问,持续。
第一if判别,第一次挪用的时分cells数组一定为null,因而,进进casBase办法:

原子更新base没啥好说的,假如更新乐成,当地挪用入手下手前往,不然进进分支外部。
甚么时分会更新失利?没错,并发的时分,好戏入手下手了,AtomicLong的处置体例是逝世轮回实验更新,直到乐成才前往,而LongAdder则是进进这个分支。
分支外部,经由过程一个Threadlocal变量threadHashCode猎取一个HashCode对象,该HashCode对象仍然是Striped64类的外部类,看界说:

有个code变量,保留了一个非0的随机数随机值。
回到add办法:

拿到该线程相干的HashCode对象后,猎取它的code变量,as[(n-1)h]这句话相称于对h取模,只不外比起取摸,由于是与的运算以是效力更高。
盘算出一个在Cells数组中领先线程的HashCode对应的索引地位,并将该地位的Cell对象拿出来更新cas更新它的value值。
固然,假如as为null而且更新失利,才会进进retryUpdate办法。
看到这里我想应当有良多人分明为何LongAdder会比AtomicLong更高效了,没错,独一会制约AtomicLong高效的缘故原由是高并发,高并发意味着CAS的失利概率更高,重试次数更多,越多线程重试,CAS失利概率又越高,酿成恶性轮回,AtomicLong效力下降。
那怎样办理?LongAdder给了我们一个十分简单想到的办理计划:削减并发,将单一value的更新压力分管到多个value中往,下降单个value的“热度”,分段更新!!!
如许,线程数再多也会分管到多个value上往更新,只必要增添cell就能够下降value的“热度”AtomicLong中的恶性轮回不就办理了吗?cells就是用来存储这个“段”的,每一个段又叫做cell,cell中的value就是寄存更新值的,如许,当我必要总数时,把cells中的value都累加一下不就能够了么!!
固然,伶俐的地方远远不单单这里,在看看add办法中的代码,casBase办法可不成以不要,间接分段更新,下去就盘算索引地位,然后更新value?
谜底是欠好,不是不可,由于,casBase操纵等价于AtomicLong中的cas操纵,要晓得,LongAdder平分段更新如许的处置体例是有害处的,分段操纵一定带来空间上的华侈,能够空间换工夫,可是,能不换就不换,要空间工夫都勤俭~!以是,casBase操纵包管了在低并发时,不会当即进进分支做分段更新操纵,由于低并发时,casBase操纵基础城市乐成,只要并发高到必定水平了,才会进进分支,以是,DougLead对该类的申明是:低并发时LongAdder和AtomicLong功能差未几,高并发时LongAdder更高效!

可是,DoungLea仍是没这么复杂,伶俐的地方还没有停止……
固然云云,retryUpdate中做了甚么事,也基础略知一二了,由于cell中的value都更新失利(申明该索引到这个cell的线程也良多,并发也很高时)大概cells数组为空时才会挪用retryUpdate,
因而,retryUpdate内里应当会做两件事:
1.扩容,将cells数组扩展,下降每一个cell的并发量,一样,这也意味着cells数组的rehash举措。
2.给空的cells变量赋一个新的Cell数组。
是否是如许呢?持续看代码:
代码对照长,酿成文本看看,为了便利人人看ifelse分支,对应的{}我在括号前面用分支XXX标注出来
能够看到,这个时分DougLea才乐意利用逝世轮回包管更新乐成~!
由于进进这个办法的概率已十分低了。
  1. finalvoidretryUpdate(longx,HashCodehc,booleanwasUncontended){inth=hc.code;booleancollide=false;//Trueiflastslotnonemptyfor(;;){Cell[]as;Cella;intn;longv;if((as=cells)!=null&&(n=as.length)>0){//分支1if((a=as[(n-1)&h])==null){if(busy==0){//TrytoattachnewCellCellr=newCell(x);//Optimisticallycreateif(busy==0&&casBusy()){booleancreated=false;try{//RecheckunderlockCell[]rs;intm,j;if((rs=cells)!=null&&(m=rs.length)>0&&rs[j=(m-1)&h]==null){rs[j]=r;created=true;}}finally{busy=0;}if(created)break;continue;//Slotisnownon-empty}}collide=false;}elseif(!wasUncontended)//CASalreadyknowntofailwasUncontended=true;//Continueafterrehashelseif(a.cas(v=a.value,fn(v,x)))break;elseif(n>=NCPU||cells!=as)collide=false;//Atmaxsizeorstaleelseif(!collide)collide=true;elseif(busy==0&&casBusy()){try{if(cells==as){//ExpandtableunlessstaleCell[]rs=newCell[n<<1];for(inti=0;i<n;++i)rs[i]=as[i];cells=rs;}}finally{busy=0;}collide=false;continue;//Retrywithexpandedtable}h^=h<<13;//Rehashh^=h>>>17;h^=h<<5;}elseif(busy==0&&cells==as&&casBusy()){//分支2booleaninit=false;try{//Initializetableif(cells==as){Cell[]rs=newCell[2];rs[h&1]=newCell(x);cells=rs;init=true;}}finally{busy=0;}if(init)break;}elseif(casBase(v=base,fn(v,x)))break;//Fallbackonusingbase}hc.code=h;//Recordindexfornexttime}
复制代码
分支2中,为cells为空的情形,必要new一个Cell数组。
分支1分支中,略庞大一点点:
注重,几个分支中都提到了busy这个办法,这个能够了解为一个CAS完成的锁,只要在必要更新cells数组的时分才会更新该值为1,假如更新失利,则申明以后有线程在更新cells数组,以后线程必要守候。重试。
回到分支1中,这里起首判别以后cells数组中的索引地位的cell元素是不是为空,假如为空,则增加一个cell到数组中。
不然更新标示抵触的标记位wasUncontended为true,重试。
不然,再次更新cell中的value,假如失利,重试。
。。。。。。。一系列的判别后,假如仍是失利,下下下策,reHash,间接将cells数组扩容一倍,并更新以后线程的hash值,包管下次更新能尽量乐成。
能够看到,LongAdder的确用了良多心机削减并发量,而且,每步都是在”没有更好的举措“的时分才会选择更年夜开支的操纵,从而尽量的用最最复杂的举措往完成操纵。寻求复杂,可是相对不粗犷。

————————————————支解线——————————————————————-
今天在coolshell投稿后(文章在这里)和左耳朵耗子复杂会商了下,发明左耳朵耗子对读者头脑的引诱仍是十分不错的,在第一次发明这个类后,对内里的完成又提出了更多的成绩,引诱人人思索,值得进修,赞一个~
我们发明的成绩有这么几个:
1.jdk1.7中是否是有这个类?
我确认后,了局以下:jdk-7u51版本上上还没有可是jdk-8u20版本上已有了。代码基础一样,增添了对double范例的撑持和删除一些冗余的代码。
2.base有无介入汇总?
base在挪用intValue等办法的时分是会汇总的:

3.base的按次可不成以互换?
左耳朵耗子,提出了这么一个成绩:在add办法中,假如cells不会为空后,casBase办法一向都没有效了?

你通过从书的数量和开发周期及运行速度来证明:net网页编程和ruby要比java简单。
柔情似水 该用户已被删除
沙发
发表于 2015-1-20 20:13:12 | 只看该作者
是一种将安全性(Security)列为第一优先考虑的语言
蒙在股里 该用户已被删除
板凳
发表于 2015-1-29 20:18:46 | 只看该作者
《JAVA语言程序设计》或《JAVA从入门到精通》这两本书开始学,等你编程有感觉的时候也可以回看一下。《JAVA读书笔记》这本书,因为讲的代码很多,也很容易看懂,涉及到面也到位。是你学习技术巩固的好书,学完后就看看《JAVA编程思想》这本书,找找一个自己写的代码跟书上的代码有什么不一样。
海妖 该用户已被删除
地板
发表于 2015-2-6 04:04:42 | 只看该作者
你可以去承接一些项目做了,一开始可能有些困难,可是你有技术积累,又考虑周全,接下项目来可以迅速作完,相信大家以后都会来找你的,所以Money就哗啦啦的。。。。。。
只想知道 该用户已被删除
5#
发表于 2015-2-7 19:23:00 | 只看该作者
如果要向java web方向发展也要吧看看《Java web从入门到精通》学完再到《Struts2.0入门到精通》这样你差不多就把代码给学完了。有兴趣可以看一些设计模块和框架的包等等。
小女巫 该用户已被删除
6#
发表于 2015-2-21 17:42:23 | 只看该作者
Java 不同于一般的编译执行计算机语言和解释执行计算机语言。它首先将源代码编译成二进制字节码(bytecode),然后依赖各种不同平台上的虚拟机来解释执行字节码。从而实现了“一次编译、到处执行”的跨平台特性。
飘飘悠悠 该用户已被删除
7#
 楼主| 发表于 2015-3-4 03:43:51 | 只看该作者
我大二,Java也只学了一年,觉得还是看thinking in java好,有能力的话看英文原版(中文版翻的不怎么好),还能提高英文文档阅读能力。
深爱那片海 该用户已被删除
8#
发表于 2015-3-10 03:38:47 | 只看该作者
还好,SUN提供了Javabean可以把你的JSP中的 Java代码封装起来,便于调用也便于重用。
小魔女 该用户已被删除
9#
发表于 2015-3-17 04:11:12 | 只看该作者
应用在电视机、电话、闹钟、烤面包机等家用电器的控制和通信。由于这些智能化家电的市场需求没有预期的高,Sun公司放弃了该项计划。随着1990年代互联网的发展
愤怒的大鸟 该用户已被删除
10#
发表于 2015-3-23 18:45:23 | 只看该作者
是一种语言,用以产生「小应用程序(Applet(s))
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-11-16 18:04

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表