原文链接:https:juejin。cnpost7090464618052583455 基于JDK1。8详细介绍了JUC下面的LongAccumulator原子类源码和原理,LongAccumulator是Java8对于原子类的增强。1LongAccumulator的原理1。1LongAccumulator的概述 publicclassLongAccumulatorextendsNumberimplementsSerializable LongAccumulator同样是来自于JDK1。8的atomic包,和LongAdder都继承了Striped64,但是LongAccumulator比LongAdder的功能更强大。 LongAccumulator相比于LongAdder,可以为累加器提供非0的初始值,后者只能提供默认的0值。另外,前者还可以指定累加规则,比如不进行累加而进行相乘,只需要在构造LongAccumulator时传入自定义的双目运算器即可,后者则只能是累加规则。 我们在此前学习了LongAdder的源码:JavaLongAdder原子累加器源码深度解析,现在我们来学习LongAccumulator的源码。它们的远啊吗和兴部分都是一样的,因此建议先学习LongAdder的源码。1。2LongAccumulator的原理1。2。1内部结构 LongAccumulator内部具有两个自己的属性,一个LongBinaryOperator类型的双目运算器实例,用于保存累加规则。一个identity用于保存指定的初始值,也是base的初始值,在reset等重置方法调用的时候也会赋值为base的值。 只有一个构造器,可以传递指定的累加规则和指定初始值。内部的LongBinaryOperator类型的属性,用于保存传入的双目运算器LongBinaryOperator是一个函数式接口,就是对累加规则的封装privatefinalLongBinaryOidentity用于保存的初始值,也是base的初始值,在reset等重置方法调用的时候也会赋值为base的值使用给定的累加器和初始值创建新实例。paramaccumulatorFunction一个双目运算器实例,可以对两个long类型的值进行计算如果为null那么在计算时将会抛出NullPointerExceptionparamidentity初始值publicLongAccumulator(LongBinaryOperatoraccumulatorFunction,longidentity){为function赋值this。functionaccumulatorF为base和identity赋值basethis。}复制代码1。2。2accumulate更新给定值 publicvoidaccumulate(longx) 具有给定值的更新。作为LongAccumulator的核心方法! accumulate方法类似于LongAdder的add方法,区别在于可以使用构造器中指定的累加器中的累加规则更新数据。 这里的LongBinaryOperator是一个JDK1。8添加的函数式接口,主要是为了lambda的调用,这个接口封装了对两个long类型参数的操作规则,通过调用applyAsLong方法对传入的参数进行操作并返回操作的结果。更新方法,与LongAdder的add方法差别在于:1。调用casBase时LongAdder传递的是bx,LongAccumulator则使用了rfunction。applyAsLong(bbase,x)自定义的规则来计算。2。调用longAccumulate时第二个参数LongAdder传递的是null,LongAccumulator传递了function累加器publicvoidaccumulate(longx){Striped64。Cell〔〕longb,v,r;Striped64。Cif((ascells)!null差别1:base要被更新为通过运算器对base和x计算出来的结果,这里也能知道传递的function不能为null(rfunction。applyAsLong(bbase,x))!b!casBase(b,r)){if(asnull(mas。length1)0(aas〔getProbe()m〕)null!(uncontended(rfunction。applyAsLong(va。value,x))va。cas(v,r)))差别2:第二个参数传递的function累加器,而不是null在longAccumulate方法中,在CAS更新value或者base的时候,会判断function是否为null,即:a。cas(va。value,((fnnull)?vx:fn。applyAsLong(v,x)))casBase(vbase,((fnnull)?vx:fn。applyAsLong(v,x)))如果不为null,那么使用制定累加器规则更新longAccumulate(x,function,uncontended);}}复制代码1。3其他操作 publiclongget() 返回当前值。类似于LongAdder的sum方法,只不过由加法变成了指定的累加规则。 publiclonglongValue() 内部调用get()方法。 publicvoidreset() 重置维持更新到标识值的变量。类似于LongAdder的reset方法,只不过base值由0变成了在构造器中传递的identity。2LongAccumulator的案例 一个指定(ab2)的累加规则的案例如下:传递一个累加器LongBinaryOperator实例LongAccumulatorlongAccumulatornewLongAccumulator(newLongBinaryOperator(){OverridepubliclongapplyAsLong(longleft,longright){指定的累加规则leftright2这里的left对应LongAccumulator中的base或者某个Cell的value,这的right对应accumulate传递的参数xreturnleftright2;}初始值为2},2);更新2longAccumulator。accumulate(6);获取结果,应该是6System。out。println(longAccumulator。get());复制代码3LongAccumulator的总结 LongAccumulator的功能更加强大,可以指定初始值和累加规则,这样看起来LongAdder更像是LongAccumulator的特例,即初始值为0,累加规则为加法。 因此如果想要使用longAccumulator实现LongAdder的功能,那么我们手动将累加规则指定为加法,并且identity指定为0即可:LongAccumulatorlongAccumulatornewLongAccumulator(newLongBinaryOperator(){OverridepubliclongapplyAsLong(longleft,longright){返回leftright,这样累加规则就和LongAdder一样了}初始值为0},0);更新6longAccumulator。accumulate(6);更新1longAccumulator。accumulate(1);获取结果,应该是7System。out。println(longAccumulator。get());复制代码 可以看到我们初始化LongAccumulator对象的代码量还是比较多的,特别是创建匿名对象的代码。上面我们说过这个LongBinaryOperator是一个函数式接口,因此我们推荐使用lambda表达式的写法:LongAccumulatorlongAccumulatornewLongAccumulator((left,right){},0);复制代码 更进一步,我们可以使用方法引用:LongAccumulatorlongAccumulatornewLongAccumulator(Long::sum,0);复制代码 它们的含义都是一样的,即采用加法运算,但是代码量却简单了许多,所以lambda还是很有必要学习的! 相关文章:Unsafe:JUCUnsafe类的原理详解与使用案例。volatile:Java中的volatile实现原理深度解析以及应用。CAS:Java中的CAS实现原理深度解析与应用案例。伪共享:Java中的伪共享深度解析以及避免方法。JMH:Java使用JMH进行方法性能优化测试。