LongAdder的由来
LongAdder是jdk1.8新增的一个原子性的操作,AtomicLong也是一个原子性的操作,AtomicLong使用CAS算法,尝试修改值,但是在修改失败后一直处于自旋修改,非常消耗CPU资源,正是这个原因,在高并发多个线程同时操作同一个资源会造成大量的线程修改值失败,大量线程处于自旋修改值状态,严重浪费CPU资源,直接降低并发性,AtomicLong在高并发多线程修改同一个资源变量,导致性能下降,那么能不能把这个同一个资源变量分解成多个资源变量,让多线程修改多个变量,这样性能不就可以提高了吗?LongAdder正是解决这个问题诞生。
LongAdder原理图与AtomicLong原理图对比
AtomicLong在高并发多线程情况下操作的同一个变量,LongAdder在高并发多线程情况下将变量分解成多个Cell变量进行操作,最终统计各个Cell的值与Base值相加得到最终的变量值,这种不能保证实时准确性,但最终一致性是可以保证的。
分析LongAdder类关系
public LongAdder() {
}
public class LongAdder extends Striped64 implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
//加操作
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);
}
}
//计算sum值,将base与cells数组的各个cell属性值相加并返回
public long sum() {
Cell[] as = cells; Cell a;
long sum = base;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
//清除cells数组的各个cell的值
public void reset() {
Cell[] as = cells; Cell a;
base = 0L;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
a.value = 0L;
}
}
}
//计算sum值并清除cells数组各个cell的值
public long sumThenReset() {
Cell[] as = cells; Cell a;
long sum = base;
base = 0L;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) {
sum += a.value;
a.value = 0L;
}
}
}
return sum;
}
}
abstract class Striped64 extends Number {
//静态内部类Cell,属性有Long 类型的value,使用 @sun.misc.Contended防止cpu缓存伪共享
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
static final int NCPU = Runtime.getRuntime().availableProcessors();
//Striped64内部维护cell数组
transient volatile Cell[] cells;
//共享变量的基础值base
transient volatile long base;
//操作各个cell以及扩容cells数组需要的锁
transient volatile int cellsBusy;
Striped64() {
}
}
LongAdder类继承了Striped64这个类,Striped64有三个属性cells数组,共享变量基础值base,操作cell和扩容需要用到的锁cellsBusy,一个内部类Cell,Cell类属性有个long类型的value,高并发情况下多线程操作的是cell单元格value进行加减操作。接下来正是进行LongAdder源码分析。
LongAdder的add方法
public void add(long x) {
/**
*as 表示cells数组
*b 表示共享变量基础值
*v 表示当前线程命中的cells数组中的某个cell的属性值
*m 表示cells数组长度
*a 表示当前线程命中的cells数组中的某个cell
**/
Cell[] as; long b, v; int m; Cell a;
/**
*条件1:cells数组不为空
* 如果为空说明可能不存在共享变量竞争(需要进行条件2进行判断)
* 如果为不为空说明存在共享变量竞争
*条件2:cas修改base值是否成功(base与x相加)
* 如果修改成功直接返回即可
* 如果修改失败存在竞争
**/
if ((as = cells) != null || !casBase(b = base, b + x)) {
//uncontended 默认设置true表示未竞争共享变量
boolean uncontended = true;
/*
*条件1: as是否空或者as数组长度是否为0
* true cells数组未初始化
* false已初始化
*条件2: 当前线程hash值与上as数组长度最终命中的cell对象是否为空
* true 命中的cell未创建需要创建
* false 命中的cell已创建
*条件3: 将当前线程命中的cell进行cas修改是否成功赋值给uncontended
* true 修改cell成功直接返回即可
* false 修改之后存在竞争,可能需要重新选址或自旋赋值
*/
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
/*
*着重分析下判断为true情况
* cells数组未初始化
* 命中的cell未创建需要创建
*修改之后存在竞争,可能需要重新选址或自旋赋值
*/
longAccumulate(x, null, uncontended);
}
}
longAccumulate源码分析★★★★★★
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
//h 表示当前线程PROBE值 为初始化默认为0
int h;
//判断当前线程PROBE值是否为0
if ((h = getProbe()) == 0) {
//随机分配当前线程的PROBE值
ThreadLocalRandom.current(); // force initialization
h = getProbe();
//这里为什么设置不存在竞争????
/*
* 只有这几种情况下才会进入longAccumulate方法内 :
1. cells数组未初始化
2. 命中的cell未创建需要创建
3. 修改之后存在竞争,可能需要重新选址或自旋赋值
* 当前线程PROBE值为0,只有两个条件才会进入,1,3两种情况,3的这种可能性大很多,
*在高并发情况下,多线程的PROBE值都是未初始化的,对应的值都是0,
*都会对cells数组0下标cell单元进行赋值,cas必定失败较多,
*这里就重新将PROBE赋值重新选址判断是否有竞争,提高性能减少自旋次数。
*/
wasUncontended = true;
}
//collide 表示是否有竞争
boolean collide = false; // True if last slot nonempty
for (;;) {
/**
*as 表示cells数组
*a 表示当前线程命中的cells数组中的某个cell
*n表示共享变量基础值
*v 表示当前线程命中的cells数组中的某个cell的属性值
**/
Cell[] as; Cell a; int n; long v;
/**
* 1. cells数组不为空 并且 长度>0
*/
if ((as = cells) != null && (n = as.length) > 0) {
/*判断当前线程PROBE与上cells数组长度-1 命中Cell是否为空
* 为空赋值
* 不为空存在竞争
*/
if ((a = as[(n - 1) & h]) == null) {
// 判断是否已上锁
if (cellsBusy == 0) { // Try to attach new Cell
//创建新的Cell对象
Cell r = new Cell(x); // Optimistically create
//再次判断是否无锁状态并且进行上锁
// true 上锁成功
// false 存在竞争 修改collide = false重新计算h值
if (cellsBusy == 0 && casCellsBusy()) {
//设置创建为false
boolean created = false;
try { // Recheck under lock
Cell[] rs; int m, j;
/*
* 判断cells是否为空
* 当前线程命中的Cell是否为空
* true 将创建的Cell赋值给命中的下标数组中
* false 存在竞争创建失败 中止本次循环
*/
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
}
}
//设置存在竞争为false重新计算h值进入下次循环
collide = false;
}
/*当前线程命中的Cell不为空存在竞争
* 判断wasUncontended是否存在竞争
* wasUncontended=true 不存在竞争
* wasUncontended=false 存在竞争设置wasUncontended=true(调用advanceProbe(h)重新选址for循环在执行一次)
*/
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
/*当前线程命中的Cell不为空存在竞争
* 判断wasUncontended=true
* 进行自旋将x赋值给当前线程命中的Cell的value
* true 赋值成功跳出循环
* false 失败是否需要扩容
*/
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
/*当前线程命中的Cell不为空存在竞争
* 判断wasUncontended=true
* 进行自旋将x赋值给当前线程命中的Cell的value失败
* 判断 数组长度是否>=cpu核数 或者 cells数组与as是否相等
* true (数组长度>=cpu核数 或者 cells和as不相等已扩容)
* true (数组长度= NCPU || cells != as)
//设置竞争为false
collide = false; // At max size or stale
else if (!collide)
//设置竞争为ture 重新计算h值 下次进入此步骤直接跳过,判断是否需要扩容
collide = true;
/*
* cellsBusy == 0 当前是否处于无锁状态
*casCellsBusy() 加锁是否成功
* true 无锁状态&上锁成功 进行扩容
* false 已加锁 或者 上锁失败 重新计算h值
*/
else if (cellsBusy == 0 && casCellsBusy()) {
try {
// DK(Double Check 判断cells与as是否相等)
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
cells = rs;
}
} finally {
// 释放锁
cellsBusy = 0;
}
//中止本次循环不计算h值
collide = false;
continue; // Retry with expanded table
}
h = advanceProbe(h);
}
/**
* 2. 处于无锁状态 & cells==as ==null(无线程初始化cells)&& 上锁成功(初始化cells数组)
*/
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table
//DK(double check cells与as是否相等 在上锁过程中可能其他线程已经初始化cells数组了)
if (cells == as) {
//初始化cells数组长度为2
Cell[] rs = new Cell[2];
//创建Cell将x赋值给value并将当前线程PROBE与上1赋值给cells数组
rs[h & 1] = new Cell(x);
//将rs引用指向cells
cells = rs;
//初始化成功
init = true;
}
} finally {
// 释放锁
cellsBusy = 0;
}
if (init)
break;
}
/**
* 3. cells为空,处于加锁状态 或者 cells != as或者在加锁过程中失败了
* 将x值加入到base中
*/
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
LongAdder初始化cells数组条件
1.cells为空,获取锁成功
LongAdder发生扩容条件
1.cells数组不为空,长度>0
2.存在Cell竞争
3.更新当前线程命中的Cell单元值失败
4.cells数组长度5.持有锁成功