更快的AtomicInteger

    xiaoxiao2024-07-11  118

    感谢同事【空蒙】的投稿

    之前看了java8的longadder实现,最近又看到一篇文章介绍longadder实现的。其实现思路也是分段,最后需要get的时候,再进行sum计算。其核心思路就是减少并发,但之前老的Atomic,难道就没有提升的空间了吗?昨晚进行了一次测试。测试代码如下:

    01/** 02* Atomically increments by one the current value. 03* 04*@return the updated value 05*/ 06 public final int incrementAndGet() { 07  08   for(;;) { 09  10    int current = get(); 11  12     int next = current + 1; 13  14     if(compareAndSet(current, next)) 15  16     return next; 17  18    } 19}

    以incrementAndGet为例,在非常高的并发下,compareAndSet会很大概率失败,因此导致了此处cpu不断的自旋,对cpu资源的浪费

    既然知道此地是高并发的瓶颈,有什么办法呢?

    01 public class AtomicBetter { 02  03       AtomicInteger ai=new AtomicInteger(); 04  05       public int incrementAndGet() { 06  07              for(;;) { 08  09                     int current =ai.get(); 10  11                     int next = current + 1; 12  13                     if(compareAndSet(current, next)) 14  15                            return next; 16  17               } 18  19        } 20  21       /** 22  23        *如果cas失败,线程park 24  25        *@paramcurrent 26  27        *@paramnext 28  29        *@return 30  31        */ 32  33       private boolean compareAndSet(intcurrent,intnext) { 34  35              if(ai.compareAndSet(current, next)) { 36  37                     return true; 38  39               }else{ 40  41                      LockSupport.parkNanos(1); 42  43                     return false; 44  45               } 46  47        } 48  49}

    很简单,当cas失败后,对线程park,减少多线程竞争导致的频繁cas失败,更进一步的导致cpu自旋,浪费cpu的运算能力。在4核虚拟机,Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz  linux 2.6.32,(注意,老版本的内核,不支持高的精度ns级) 进行测试,同样都起4个线程,每个线程里面对AtomicInteger进行5kw次的incrementAndGet。原生的AtomicInteger,耗时14232ms,进行了35870次上下文切换,总共87967770955次时钟周期。那prak 1ns下呢,耗时5195ms,进行了19779次上下文切换,总共36187480878次时钟周期,明显性能上比原生的AtomicInteger更好,那这个park多少合适呢?那就只有人肉测试了

    parktime(ms)context-switchescyclesAtomicInteger1423235,87087,967,770,9551ns519519,77936,187,480,87810ns505020,22334,839,351,263100ns523820,72437,250,431,417125ns453647,47926,149,046,788140ns4008100,02218,342,728,517150ns3864110,72016,146,816,453200ns3561125,69411,793,941,243300ns3456127,07210,200,338,988500ns3410132,1639,545,542,3401us3376134,4639,125,973,2905us3383122,7959,009,226,31510us3367113,9308,905,263,507100us339150,9258,359,532,733500us345617,2258,096,303,1461ms348610,9827,993,812,19810ms34562,6007,845,610,195100ms35551,0207,804,575,756500ms38548227,814,209,077

     

     

     

    本机环境下,park 1ms下,相对耗时,cs次数来说是最好的。因此这种优化要达到最佳效果,还要看cpu的情况而定,不是一概而定的

    两个问题:

    1、cas失败对线程park的副作用是什么。

    2、如果park的时间继续加大,那会是这么样的结果呢。

    文章转自 并发编程网-ifeve.com

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)