public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
//Cell数组为null时,会尝试CAS对basecount+x。若不成功进入if
//这里可以看到cells!=null条件的优先级高,因此Longadder会优先选择在cells中更新。
if ((cs = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
//CASE1:Cell数组为null
//CASE2:Cell数组所在索引上的Cell对象是null
//CASE3:1、2都不是null,就尝试CAS更改该Cell中的value,并且更改失败
//以上三种情况都会进入if
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[getProbe() & m]) == null ||
!(uncontended = c.cas(v = c.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {
int h;
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
done: for (;;) {
Cell[] cs; Cell c; int n; long v;
//CASE1:Cell数组已经初始化,进入if
if ((cs = cells) != null && (n = cs.length) > 0) {
//1.1:但是在该索引上的Cell对象为null,就需要初始化
// cellsBusy是对整个数组的锁标记,0是无锁,1是锁给占用
if ((c = cs[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create
if (cellsBusy == 0 && casCellsBusy()) {
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;
break done;
}
} finally {
cellsBusy = 0;
}
continue; // Slot is now non-empty
}
}
collide = false;
}
//1.2:这个wasUncontended看不懂有什么用
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//1.3:尝试去更新Cell中的value值,成功就结束循环break
else if (c.cas(v = c.value,
(fn == null) ? v + x : fn.applyAsLong(v, x)))
break;
//1.4:作为扩容上限的限制条件
else if (n >= NCPU || cells != cs)
collide = false; // At max size or stale
//1.5:其实隐含为下一个elseif的条件,只是为了多循环一次
else if (!collide)
collide = true;
//1.6:这时候1.1~1.5都不符合,1.6进行扩容时,已经CAS了2次,需要扩容
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == cs) // Expand table unless stale
cells = Arrays.copyOf(cs, n << 1);
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
h = advanceProbe(h); //每循环一次都会重新hash
}
//CASE2:Cell数组还没以后初始化,若拿到锁标记后,就去初始化
else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
try { // Initialize table
if (cells == cs) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
break done;
}
} finally {
cellsBusy = 0;
}
}
// Fall back on using base
//CASE3:这是针对Cell数组没有初始化,又不满足CASE2,因为已经有线程正在初始化,无法
//拿到锁标记进入CASE2,这时候不能让他闲着,干脆就让他去CAS更新BaseCount的值
else if (casBase(v = base,
(fn == null) ? v + x : fn.applyAsLong(v, x)))
break done;
}
}
总结:
add跳入LongAccumulate三个”或“条件:
1、Cell数组未初始化
2、在所求得的索引的数组元素未初始化
3、对数组元素的CAS失败
LongAccumulate里循环内的三个大分支:
CASE1:Cell数组初始化了。
那就针对2、3条件包括进行Cell初始化、重新哈希、扩容、数组元素CAS的操作并且break。
CASE2:能拿到cellsBusy=0,且Cell数组未初始化。
那就进行数组初始化,然后继续下一轮循环
CASE3:Cell数组未初始化,但锁又被执行CASE2情况的线程占用中
此时有线程在进行线程初始化,就干脆尝试CAS更新BaseCount的值
意义:高并发下更加优秀的计数方案
AtomicLong原子类在高并发的场景下,虽然他能够解决线程安全的问题,效率不高,会有大量的线程CAS失败,x个线程同时进行CAS,有x-1的CAS都会失败,因此CAS+自旋反而成为性能瓶颈
LongAdder实现原理是采用空间换时间的思想,用Cell数组来分散所有线程访问Basecount的压力,不过这里有个叫做”内存伪共享“的概念。
Cell数组是内存中连续的一块空间,这块空间倘若作为一个缓存块,缓存到Cache中去,如果修改其中某个Cell对象,会导致缓存块的失效,很显然会带来严重的缓存开销。解决方案就是“填充”,让每个Cell处于不同的缓存块中,这里一样贯彻的是空间换时间的理念。
。