LongAdder简介
LongAdder是JDK提供的用于并发计数的一个类,这个类继承自Striped64
Striped64中有两个主要属性base和cell数组
当线程对LongAdder进行写入时,会写入其中的base中
如果有线程竞争,写入base失败,就会创建cell数组,写入对应位置的cell中
LongAdder属性
LongAdder的属性均是继承自Striped64,cell类中包装了一个value,用来存储计数的值
static final class Cell {
//cell中的值
volatile long value;
//构造方法
Cell(long x) { value = x; }
//用CAS方式更新value
final boolean cas(long cmp, long val) {
return U.compareAndSwapLong(this, VALUE, cmp, val);
}
final void reset() {
U.putLongVolatile(this, VALUE, 0L);
}
final void reset(long identity) {
U.putLongVolatile(this, VALUE, identity);
}
//Unsafe对象
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long VALUE;
//获取到value对应的偏移量,用于CAS操作
static {
try {
VALUE = U.objectFieldOffset
(Cell.class.getDeclaredField("value"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
}
//cell数组,当线程真正写入时才会初始化
transient volatile Cell[] cells;
//线程会通过CAS方式更新该值
transient volatile long base;
//一把特殊的锁:用来表示cell数组或者cell是否正在初始化,正在扩容
//0:表示当前锁空闲
//1:表示当前锁已经被持有
transient volatile int cellsBusy;
/**
* Package-private default constructor.
*/
Striped64() {
}
线程的probe值和casCellsBusy()方法
当线程需要把数写入cell数组中时,probe值作用和hash值一样,用来确定线程写入数组的哪个cell
/**
* CASes the cellsBusy field from 0 to 1 to acquire lock.
* 通过CAS方式获取锁
*/
final boolean casCellsBusy() {
return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 0, 1);
}
/**
* Returns the probe value for the current thread.
* Duplicated from ThreadLocalRandom because of packaging restrictions.
*
* 获取当前线程的probe值
*/
static final int getProbe() {
return UNSAFE.getInt(Thread.currentThread(), PROBE);
}
/**
* Pseudo-randomly advances and records the given probe value for the
* given thread.
* Duplicated from ThreadLocalRandom because of packaging restrictions.
*
* 重置当前线程的probe值
*/
static final int advanceProbe(int probe) {
probe ^= probe << 13; // xorshift
probe ^= probe >>> 17;
probe ^= probe << 5;
UNSAFE.putInt(Thread.currentThread(), PROBE, probe);
return probe;
}
解析add()
add()方法正是LongAdder用来增加计数的方法
public void add(long x) {
// as:当前的cell数组 b:当前的base v:当前线程对应的cell的值 m:当前cell数组的长度减一 a; 当前线程对应的cell
Cell[] as; long b, v; int m; Cell a;
//cell数组是否初始化,没有初始化则更新base值
if ((as = cells) != null || !casBase(b = base, b + x)) {
//cas更新线程对应的cell的value值是否成功
boolean uncontended = true;
//当前cell数组是否初始化
if (as == null || (m = as.length - 1) < 0 ||
//线程对应的cell是否初始化
(a = as[getProbe() & m]) == null ||
//更新cell中value的值是否成功
!(uncontended = a.cas(v = a.value, v + x)))
// 这个方法下面有解释
longAccumulate(x, null, uncontended);
}
}
值得注意的是
if ((as = cells) != null || !casBase(b = base, b + x))
当cells数组初始化后,线程将不再竞争写入base
解析longAccumulate()
进入此方法的情况:
-
数组尚未初始化,线程写入base失败
-
cell数组已经初始化,线程对应位置的cell尚未初始化
-
cell已经初始化,CAS操作cell更新value失败
//wasUncontended什么时候为true:cell数组为null,或者cell数组中线程对应的cell为null
// false:线程CAS更新cell的value值失败
final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
//线程的probe值
int h;
//线程尚未分配probe值
if ((h = getProbe()) == 0) {
//初始化probe
ThreadLocalRandom.current(); // force initialization
h = getProbe();
//为什么是true?
//线程probe值未分配时为0,肯定时写入cell[0],这是特殊情况,不算竞争
wasUncontended = true;
}
//可以理解为是否需要扩容
boolean collide = false;
//自旋
done: for (;;) {
//as:cell数组 a:线程对应的cell n:cell数组长度 v:更新期望值
Cell[] as; Cell a; int n; long v;
//情况一:cell数组已经被初始化
if ((as = cells) != null && (n = as.length) > 0) {
//情况a:线程对应的cell没有初始化,要初始化自己的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()) {
try {
//rs:当前cell数组 m:数组长度 j:线程对应的数组下标
Cell[] rs; int m, j;
//再次检查线程对应的cell
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
//初始化自己的cell
rs[j] = r;
break done;
}
} finally {
//释放锁
cellsBusy = 0;
}
//当上面检查cell不是null时,就跳过,再次自旋
continue; // Slot is now non-empty
}
}
//目前不扩容
collide = false;
}
//情况b:线程CAS更新cell的value值失败:把wasUncontended置为true
//后面会重置线程probe值,即让线程更新cell数组另一个cell
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//情况c:线程CAS更新cell的value值
else if (a.cas(v = a.value,
(fn == null) ? v + x : fn.applyAsLong(v, x)))
break;
//情况d:n>=NCPU,cell数组已经达到最大长度,不能再扩容了
cells != as,当前数组已经扩容过了,不能再扩容了
else if (n >= NCPU || cells != as)
//扩容置为false,继续自旋尝试更新
collide = false; // At max size or stale
//情况e:上面cell更新失败,并且可以扩容
else if (!collide)
collide = true;
//情况f:拿锁尝试扩容
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as)
//扩容,数组为原来2倍
cells = Arrays.copyOf(as, n << 1);
} finally {
//释放锁
cellsBusy = 0;
}
//扩容置为false
collide = false;
continue; // Retry with expanded table
}
//竞争比较大,重置线程probe值,即让线程更新cell数组另一个cell
h = advanceProbe(h);
}
//情况二:cell数组没有初始化,要尝试拿锁并且初始化cell数组
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
//初始化cell数组
try { // Initialize table
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
break done;
}
} finally {
//释放锁
cellsBusy = 0;
}
}
//情况三:前面都失败了,更新base
else if (casBase(v = base,
(fn == null) ? v + x : fn.applyAsLong(v, x)))
break done;
}
}
总结一下:这个方法就是初始化cell数组和数组中的各个cell,并在竞争激烈时扩容
sum()方法
获取计数的值,将base和cell数组中存储的value相加 得到的只是一个大概准确的值
public long sum() {
//as:当前cell数组
Cell[] as = cells; Cell a;
long sum = base;
if (as != null) {
//遍历cell数组
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
//将cell中的value值累加到sum中
sum += a.value;
}
}
return sum;
}