37

高并发下比 AtomicLong 性能更好的 LongAdder

 4 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzA3MjIyMzgzMQ%3D%3D&%3Bmid=2257484211&%3Bidx=1&%3Bsn=0c853e0e4536d43ad86292353053b5df&%3Bchksm=9c5b448bab2ccd9d440de913098b1443dea76df9d7f05e260653094abf6d24ad4880033e902f&%3Btoken=1720990248&%3Blang=zh_CN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

LongAdder原理

有了AtomicLong之后,为什么还需要有LongAdder呢?LongAdder主要是为了在多线程并发高场景下使用,性能比AtomicLong更好。

AtomicLong在多线程下一直都是在更新一个热点数据value值,而LongAdder就通过将单个节点的并发修改分散到多个节点上,就相当于是在更新不同的value值,冲突少。

这个类里最主要的两个方法,add和sum。

先看下add方法实现:

7zAFryv.png!mobile

1.如果cell为空,没有冲突的情况下,都是通过更新base值就成功,这个是为了在低并发的情况下性能也能和AtomicLong差不多。

2.如果cell不为空,则会根据当前线程的threadLocalRandomProbe值取模计算在cell数组中的位置,如果数组所在位置cell不为空,则cas方式更新相应的value值。

3.如果为空则会最后执行longAccumulate方法。

longAccumulate逻辑(代码比较长,考虑扩容等并发情况,大概说下逻辑):

1.如果当前线程定位到cell数组的位置为null,则会创建一个新的cell并将本次值赋值给cell的value值

2.如果不为空,则尝试通过定位到的cell去cas更新

sum方法实现:

qeYNbim.png!mobile

这里就会遍历Cell数组里的所有值,加上base全部加一起返回。

性能测试

网上别人这个测试已经说的比较详细了

http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/

但是这里并不要看到里面第一张图,就以为是在一个线程的时候AtomicLong性能高,而二个线程的时候AtomicLong就比LongAdder差很多了,并不是的。关键这里还是得看他的测试代码,他测试代码里面主要是每个线程会for循环加1000000次,所以跟一个线程加一次还是有区别的。

是不是可以废弃掉AtomicLong,全使用LongAdder呢

答案并不是的:

  • 首先,AtomicLong API比较丰富,提供了incrementAndGet\getAndAdd等很方便的操作,而LongAdder主要是add\sum(用于取值)方法

  • 由于LongAdder的取值sum方法是遍历了所有的cell,然后值相加得到,所以可能存在在获取值的时候cell的值有并发更新,统计的值有误差

总结

线程冲突不高情况下,使用AtomicLong是比较好,冲突高情况下使用LongAdder性能更好。相应的还有AtomicDouble vs DoubleAdder

文章推荐 Mybatis报错org.apache.ibatis.ognl.NoSuchPropertyException分析 你真的了解线程池ThreadPoolExecutor吗? MySQL手记23 -- MySQL实例运行情况巡检

RVnaQfm.gifjaA7BzI.gif

扫描右方二维码                关注猿的话

viUJZj.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK