一、LongAdder的实现原理
LongAdder的基本思路就是分散热点,将value值分散到一个数组中,这个数组是他的父类Striped64类的一个变量transient volatile Cell[] cells,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回
举个例子:
比如3个线程对变量sum = 0进行三次累加,每次加1.
则LongAdder是根据Cell[] cells数组
比如线程1在index0,也就是base中,线程2为index1,线程3为index2,每个位置的元素为1,即cells = [1,1,1]。
那么longAdder的值就遍历整个cells数组,任何累加就得到其最终值3.
二、源码分析
LongAdder继承了哪些类
public class LongAdder extends Striped64 implements Serializable {
//可以看出继承了Striped64类
Striped64类中的两个核心变量
//用来存放各个线程更改的值,当非空时,size是2的幂。
transient volatile Cell[] cells;
//基本值,主要在没有争用时使用,但也可作为表初始化竞争期间的回退,通过CAS更新
transient volatile long base;
Striped64类中的Cell内部类
//@sun.misc.Contended作用伪防止伪共享
@sun.misc.Contended static final class Cell {
//存放long值的变量
volatile long value;
//构造方法
Cell(long x) {
value = x;
}
//CAS方法
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// Unsafe mechanics
//unsafe常量,设置为使用Unsafe.compareAndSwapInt进行更新
private static final sun.misc.Unsafe UNSAFE;
//Long的值在内存地址的偏移量
private static final long valueOffset;
static {
try {
//实例化UNSAFE对象
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
//获取valueOffset的偏移量
valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
LongAdder常用方法源码分析
①:add()方法
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
/**
* 1.先判断cells数组有没有初始化
* 2.然后用casBase来对base偏移量进行CAS操作,如果没有线程竞争就更新成功然后退出
* 3.若有线程竞争,则进入下一个if,uncontended是表示无竞争的意思
*/
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
/**
* as == null || (m = as.length - 1) < 0 表示如下:
* 先判断as也就是cells数组有没有进行初始化,如果没有初始化就直接进入longAccumulate方法初始化
*/
if (as == null || (m = as.length - 1) < 0 ||
/**
* a = as[getProbe() & m]) == null表示如下:
* 1.先根据线程的hash值(getProbe()方法表示获取其线程hash值)& m得到index
* 2.然后判断此数组下标中是否有元素,如果没有元素就直接进入longAccumulate方法
*/
(a = as[getProbe() & m]) == null ||
/**
* !(uncontended = a.cas(v = a.value, v + x))表示如下:
* 1.若有元素,则通过CAS将需要加上的值x加到此数组下标上
* 2.若CAS返回失败则直接进入longAccumulate方法
*/
!(uncontended = a.cas(v = a.value, v + x)))
//处理涉及初始化、调整大小、创建新单元格和/或争用的更新情况。
//这种方法遇到了乐观重试代码常见的非模块化问题,它依赖于重新检查的读集。
longAccumulate(x, null, uncontended);
}
}
我们继续进入longAccumulate()方法(在Striped64类中)
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
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) {
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;
}
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;
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
else if (!collide)
collide = true;
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
}
h = advanceProbe(h);
}
//第二种情况
//cellBusy==0 未加锁 && cells == as 未新建 && casCellsBusy() 开始加锁
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;
}
//此为加锁失败的情况
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
我们继续看longAccumulate()方法的第一种情况
我们先看第一种情况下的第一种情况
// 累加单元的数组创建好了,但未必累加单元就创建好了,因为是懒惰初始化,累加单元是用到时才创建
// (as = cells) != null cells存在
if ((as = cells) != null && (n = as.length) > 0) {
// (a = as[(n - 1) & h]) == null cell对象未创建
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create
// cellsBusy == 0 未上锁 && casCellsBusy() 尝试加锁
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
// 最后再判断以下cell对象是否存在,要考虑到其他线程是否会已经生成了cell对象
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;
}
}
我们再来看其他情况
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;
// n >= NCPU 如果超过cpu的上限了,就不需要扩容了
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
else if (!collide)//超过了CPU上限的情况
collide = true;
//加锁成功的情况
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
}
// 改变线程对应的cell对象,因为自己这个累加单元老是失败,可能另外一个累加单元没人用,就换为另外一个累加单元
h = advanceProbe(h);
②:sum()方法
// 累加和,获取LongAdder的最终结果
public long sum() {
Cell[] as = cells; Cell a;
// base为基础累加单元
long sum = base;
if (as != null) {
// 通过遍历cells数组,然后累加各个下标上的值得到最终的结果
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}