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是做到最终一致性。