LongAdder为什么快

AtomicLong与LongAdder

同样是原子Long类的加1操作,LongAdder运行速度比AtomicLong速度要快,因为LongAdder使用Base+西格玛Cell累积单元。如果想使用Long的其他运算,使用LongAccumulator具体实现LongBinaryOperator接口(1.8函数式编程)。

为什么LongAdder比较快?

理论:

通过分散热点的思想,将value值分散到Cell数组中,不同线程通过hash的分配方式到不同的Cell中,各个线程对自己的cell进行CAS操作。可以理解为AtomicLong多个线程争一个自旋锁,LongAdder多个线程争多个自旋锁。

从源码看:

最主要的是LongAdder中,有base和cells单元格。为Striped64父类的成员。

 transient volatile Cell[] cells;
 ​
 transient volatile long base;

主要核心方法有三个

add(long x)

longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended)

sum()

add()方法

 public void add(long x) {
     Cell[] as; long b, v; int m; Cell a;
     if ((as = cells) != null || !casBase(b = base, b + x)) {
         boolean uncontended = true;
         if (as == null || (m = as.length - 1) < 0 ||
             (a = as[getProbe() & m]) == null ||
             !(uncontended = a.cas(v = a.value, v + x)))
             longAccumulate(x, null, uncontended);
     }
 }

对if中的条件开始分析:

第一个if中的条件

(as = cells) != null

一开始线程竞争不激烈cells为空所以为false。


!casBase(b = base, b + x)

查看casBase()方法,该方法为Striped64父类的方法:

为UnSafe类的CAS方法,为cas底层依赖;

 final boolean casBase(long cmp, long val) {
     return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
 }

对base进行CAS操作,如果修改成功,整个if返回false,就不进入if里的操作,表示低并发的情况,如果修改失败,表示高并发的情况,进入if中的操作。


如果进入了if,boolean uncontended = true;将争夺标志变为true;uncontended为非争夺,true/false表示是否为非争夺状态。

第二个if

as == null

判断cells是否为空。如果为null,这个表达式就为true;


(m = as.length - 1) < 0

判断cells的长度是否小于2。如果小于2,这个表达式就为true;

m为什么要等于as.length - 1,因为as的长度为2的幂次,减一后m的二进制刚好为01111……这种规律。下面进行&位运算时,好让计算的数计算后为:二进制数头去掉1,而其他位数值又不会改变。


(a = as[getProbe() & m])

如果进到了这个条件的判断,说明已经有了cells实例对象。并且cells.length>=2。

getProbe() & m,获取属于线程的hash码,并对m进行二进制与运算,得到一个cells中的一个Cell对象。

(a = as[getProbe() & m]) == null 判断取到的cell对象是否为null,如果为null,这个表达式就为true;


a.cas(v = a.value, v + x)

对cell中的值以CAS的方式修改。

!(uncontended = a.cas(v = a.value, v + x))如果修改失败,这个表达式就为true;


只要上面有一个条件为true就进入longAccumulate方法:

longAccumulate方法

主要分为4个部分(2、3部分对cells进行扩容和新建)

第一个部分是保证当前线程有一个非0的轻量级的哈希码:

第二个部分是cells扩容或进行修改:

 boolean collide = false;                // True if last slot nonempty
 for (;;) {
     Cell[] as; Cell a; int n; long v;
     if ((as = cells) != null && (n = as.length) > 0) {//第二部分,下面分为6个小部分
         //2.1部分,对cells槽中进行创建Cell对象。
         if ((a = as[(n - 1) & h]) == null) {
             if (cellsBusy == 0) {       // Try to attach new Cell
                 Cell r = new Cell(x);   // Optimistically create
                 if (cellsBusy == 0 && casCellsBusy()) {//锁起来对cells槽中进行创建Cell对象。
                     boolean created = false;
                     try {               // Recheck under lock
                         Cell[] rs; int m, j;
                         if ((rs = cells) != null &&
                             (m = rs.length) > 0 &&
                             rs[j = (m - 1) & h] == null) {
                             rs[j] = r;
                             created = true;
                         }
                     } finally {
                         cellsBusy = 0;//解锁
                     }
                     if (created)//判断槽中的Cell对象是否创建成功,如果为false说明该槽被其他线程抢先创建。
                         break;
                     continue;           // Slot is now non-empty,下一次就会为扩容或者修改操作了。
                 }
             }
             collide = false;
         }
         //2.2部分,说明hash值冲突,是baseCas修改失败后第一次进入循环,直接跳出这6小步,刷新hash值,继续下一次循环。
         else if (!wasUncontended)       // CAS already known to fail
             wasUncontended = true;      // Continue after rehash
         //2.3部分,对cell进行cas修改
         else if (a.cas(v = a.value, ((fn == null) ? v + x ://进行cas修改,修改成功退出循环。
                                      fn.applyAsLong(v, x))))
             break;
         //2.4部分
         else if (n >= NCPU || cells != as)//判断cells容量是否已经大于CPU的最大核数。将扩容意向设置为collide = false
             collide = false;            // At max size or stale
         //2.5部分
         else if (!collide)              //前面条件都不满足,设置扩容意向collide = true,然后换个hash值进行扩容
             collide = true;
         //2.6部分,cells进行扩容
         else if (cellsBusy == 0 && casCellsBusy()) {//锁起来对cells进行扩容。
             try {
                 if (cells == as) {      // Expand table unless stale
                     Cell[] rs = new Cell[n << 1];//二进制向左移1位,表示每次扩容2倍。
                     for (int i = 0; i < n; ++i)//复制扩容
                         rs[i] = as[i];
                     cells = rs;
                 }
             } finally {
                 cellsBusy = 0;//解锁
             }
             collide = false;
             continue;                   // Retry with expanded table
         }
         //上面6小步有一步执行完后,刷新线程的hash值
         h = advanceProbe(h);
     }else if(...){//连接下方第三个部分的else if
         //第三个部分
     }else if(...){
         //第四个部分
     }

第三个部分是新建cells数组容量为2:

在创建cells时,用casCellsBusy()保证安全。

第四个部分是进行casBase:

所有if都不行,再试试改变base,如过还是不行,继续循环。


sum()方法

其实是base与cells中value累加。线程不安全,需要等待执行的线程完毕。

LongAdder是做到最终一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值