LongAdder和AtomicInteger的作用就是在多线程并发的环境下保证数值计算正确。
优点:在多线程竞争激烈的情况下相比于AtomicInteger可减少cpu资源消耗
缺点:数值实时性不高,内存消耗较多
优点:比LongAdder消耗内存少,且数值实时性高
缺点:竞争激烈的情况下 只有一个线程能修改成功,太浪费cpu资源。
用一个例子来说明LongAdder执行流程
public static void main(String[] args) throws InterruptedException {
LongAdder longAdder = new LongAdder();
for (int i = 0; i < 10; i++) {
new Thread( () ->{
longAdder.add(1); //10个线程累加1
}).start();
}
Thread.sleep(1000);
long sum = longAdder.sum();//计算总和
System.out.println(sum);
}
主要方法就是add() 和 sum()
先说明几个变量
/**
* Table of cells. When non-null, size is a power of 2.
* 单元格表。非空时,大小为2的幂。
*/
transient volatile Cell[] cells;
/**
* Base value, used mainly when there is no contention, but also as
* a fallback during table initialization races. Updated via CAS.
* 基本值,主要在没有争用时使用,但也用作表初始化竞争期间的回退。通过CAS更新
*/
transient volatile long base;
/**
锁,值为0时说明被其他线程占用
*/
transient volatile long cellsBusy;
sum方法是用于计算cell数组中的数值总和,很明显能看到,通过for循环来对cell中的数值进行累加后返回。
public long sum() {
Striped64.Cell[] var1 = this.cells;
long var3 = this.base;// 没有多线程竞争时,使用此变量进行累加。
if (var1 != null) {
for(int var5 = 0; var5 < var1.length; ++var5) {
Striped64.Cell var2;
if ((var2 = var1[var5]) != null) {
var3 += var2.value;
}
}
}
return var3;
}
在多线程场景下,有可能数组下标0,1,2这三个位置的数值已经累加完成,此时正在累加数组下标为4的数值,那么0,1,2 这三个位置的数值如果再有其他线程去修改,那么在本次执行的sum() 中,是不会进行累加的。这就解释了为什么sum数值实时性不高。
public void add(long var1) {
Striped64.Cell[] var3;
long var4;
if ((var3 = this.cells) != null //判断cells数组是否为空,为空说明是未初始化或者不是多线程场景
|| !this.casBase(var4 = this.base, var4 + var1)) // casBase方法内部通过cas进行修改值,修改成功说明没有其他线程竞争
{
boolean var10 = true;
long var6;
int var8;
Striped64.Cell var9;
if (var3 == null //说明未初始化cells数组
|| (var8 = var3.length - 1) < 0 //说明未初始化cells数组
|| (var9 = var3[getProbe() & var8]) == null // 通过getProbe() & var8计算当前线程的数组下标
|| !(var10 = var9.cas(var6 = var9.value, var6 + var1))) { // 再次cas修改值
this.longAccumulate(var1, (LongBinaryOperator)null, var10); // 上面条件失败后进入核心方法,初始化cells数组或者扩容等
}
}
}
final boolean casBase(long var1, long var3) {
return UNSAFE.compareAndSwapLong(this, BASE, var1, var3);
}
static final int getProbe() {
return UNSAFE.getInt(Thread.currentThread(), PROBE);
}
/**
* Handles cases of updates involving initialization, resizing,
* creating new Cells, and/or contention. See above for
* explanation. This method suffers the usual non-modularity
* problems of optimistic retry code, relying on rechecked sets of
* reads.
*
* @param x the value
* @param fn the update function, or null for add (this convention
* avoids the need for an extra field or function in LongAdder).
* @param wasUncontended false if CAS failed before call
*/
/**
处理涉及初始化、调整大小、创建新单元格和或争用的更新情况。请参阅上面的说明。
这种方法遇到了乐观重试代码的常见非模块化问题,依赖于重新检查的读取集
@param x更新函数的值
@param fn,或add为null(此约定避免了在LongAdder中需要额外的字段或函数)
@param wasUnconsent 如果CAS在调用前失败,则为false
*/
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
// 用于通过 h & cells.length -1 计算线程操作的位置
int h;
// 判断线程的probe是否初始化
if ((h = getProbe()) == 0) {
// 初始化线程probe
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
// 用于记录cell数组当前是否存在空的桶,false表示大概率存在空桶,及时满足拓容条件也暂时先不拓容
boolean collide = false; // True if last slot nonempty 如果最后一个插槽为非空,则为True
for (;;) {
Cell[] as; Cell a; int n; long v;
// cells 是否为空
if ((as = cells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {
// cells 不为空,并且线程操作桶为null,则new Cell(x)并更新
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create
if (cellsBusy == 0 && casCellsBusy()) {
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)
break;
continue; // Slot is now non-empty
}
}
collide = false;
}
// 在LongAdder.add()方法中已经cas过一次,如果probe没有更新,则直接进行a.cas操作大概率失败,则加了此次判断
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
// cell数组长度大于cpu数,则后续不再拓容
// cells != as 正在拓容,则下次循环有空桶的概率较大 将collide = false,下次执行到此处则会advanceProbe(h) 一次而非直接拓容
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
else if (!collide)
collide = true;
// 拓容,每次为 数组长度 << 1
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
// 每次失败后都会修改probe值,重新进入循环,而非probe不变
h = advanceProbe(h);
}
// cells 为null,则初始化cells,初始size为2 ,casCellsBusy是使用cas加锁,表明正在扩容
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table 初始化表
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
// cells 为null ,并且cellsBusy 锁竞争失败,则其他线程正在初始化,尝试casBase
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
LongAdder在单线程的时候通过base变量存储值,而多线程竞争情况下,使用数组分摊多次计算,让多个线程通过计算探针值