前言AtomicInteger和AtomicLong使用非阻塞CAS算法原子更新一个变量,比synchronized阻塞算法有更好的性能,但是在高并发下,大量线程更新一个变量在同时,由于同一时间只能有一个线程成功,大部分线程在尝试更新失败后会通过自旋重试,严重占用CPU的时间片。AtomicLong的实现示意图:LongAdder是JDK8中新增的一个原子操作类。它提供了一种新的思维方式。既然AtomicLong的性能瓶颈是大量线程同时更新一个变量造成的,那这个变量能不能拆分出来呢?,变成多个变量,然后让线程去竞争这些变量,最后合并?LongAdder的设计精髓就在这里。通过将变量拆分成多个元素,降低变量的并发度,最后合并元素,变相的减少了CAS失败的次数。LongAdder的实现示意图:公共方法publicclassLongAdderextendsStriped64implementsSerializable{//构造方法publicLongAdder(){}//加1操作publicvoidincrement();//减1操作publicvoiddecrement();//获取原子的值variablepubliclonglongValue();}下面举一个简单的例子模拟50个线程同时更新.LongAdder;publicclassMain{publicstaticvoidmain(String[]args){LongAdderadder=newLongAdder();ExecutorServicethreadPool=Executors.newFixedThreadPool(20);for(inti=0;i<50;i++){Runnabler=()->{adder.add(1);};threadPool.execute(r);}threadPool.shutdown();//如果关闭线程池后所有任务都执行完毕,isTerminated()返回truewhile(!threadPool.isTerminated()){System.out.println(adder.longValue());break;}}}输出结果为50,如果你不是fa熟悉线程池的可以参考我另一篇讲线程池原理分析类图的文章。LongAdder内部维护了一个Cell类型的数组,其中Cell是Striped64中的静态内部类。Cell类abstractclassStriped64extendsNumber{@sun.misc.ContendedstaticfinalclassCell{volatilelongvalue;Cell(longx){value=x;}finalbooleancas(longcmp,longval){returnUNSAFE.compareAndSwapLong(this,valueOffset,cmp,val);}}}Cell用于封装对于拆分后的元素,内部使用一个value字段来存储当前元素的值,需要合并时,将所有Cell数组中的值累加起来。Cell内部使用CAS操作来更新value值。不熟悉CAS操作的同学可以参考我的另一篇文章探讨CAS的实现原理。你可以注意到Cell类被@sun.misc.Contended注解修改了。这个注解是为了解决虚假分享的问题。什么是虚假分享?一个缓存行可以存储多个变量(填充当前缓存行的字节数);而CPU对缓存的修改是以缓存行的最小单位为单位的。在线程的情况下,如果需要修改“共享同一缓存行的变量”,就会在不经意间影响到彼此的性能,这就是虚假共享(FalseSharing)。不了解虚假共享的同学可以参考这位大佬的文章虚假共享(FalseSharing)的底层原理及其解决方法。LongAdder使用的是Cell数组,数组的元素是连续的,所以多个Cell对象共享一个cacheline的情况很常见,所以这里@sun.misc.Contended注解对单个Cell元素进行字节填充,保证一个Cell对象占用一个cacheline,即填充到64字节。关于如何确定一个对象的大小,可以参考我的另一篇文章对象的内存布局,如何确定对象的大小,这样就可以计算出需要填充多少字节。longValue()longValue()返回累加值publiclonglongValue(){returnsum();}publiclongsum(){Cell[]as=cells;Cella;longsum=base;//当Cell数组不为null时,累加后返回,否则直接返回基准数baseif(as!=null){for(inti=0;i
