最近喜欢上阅读国外大牛写的源码,真的感觉每一步都是精华,膜拜膜拜。
特此将代码的每一步解析总结出来分享给大家~~~
/**
* Adds the given value.
*
* @param x the value to add
*/
public void add(long x) {
//as: 表示cells引用
//b: 表示获取的base值
//v: 表示 期望值
//m: 表示cells数组长度
//a: 表示当前线程命中的cell单元格
Cell[] as; long b, v; int m; Cell a;
//条件一:true -> cells数组已经初始化了,当前线程需要将数据写入到对应的cell中
// false -> 表示cells未初始化,当前所有线程应该将数据写到base中
//条件二:true -> 表示发生竞争了,可能需要 重试 或者 扩容
// false -> 表示当前线程cas替换数据成功
if ((as = cells) != null || !casBase(b = base, b + x)) {
//进入的条件:
//1.cells数组已经初始化了,当前线程需要将数据写入到对应的cell中
//2.表示发生竞争了,可能需要 重试 或者 扩容
//是否有竞争:true -> 没有竞争
// false -> 有竞争
boolean uncontended = true;
//条件一:true -> cells 未初始化,即多线程写base发生了竞争
// false -> cells 已经初始化,继续执行 条件二
//条件二:getProbe() 获取当前线程的hash值 m表示cells数组长度 - 1 cells数组长度一定是2的次方数 15 = b1111
// true -> 说明当前线程对应下标的cell为空,继续执行 longAccumulate()方法
// false -> 说明当前线程对应下标的cell不为空,继续执行 条件三
//条件三:true -> 说明cas失败,意味着当前线程对应的cell有竞争
// false -> 说明cas成功
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
//调用的条件:
//1.cells 未初始化,即多线程写base发生了竞争[重试 or 初始化cells]
//2.说明当前线程对应下标的cell为空
//3.说明cas失败,意味着当前线程对应的cell有竞争[重试 or 扩容cells]
longAccumulate(x, null, uncontended);
}
}
/**
* 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
*/
//调用的条件:
//1.cells 未初始化,即多线程写base发生了竞争[重试 or 初始化cells]
//2.说明当前线程对应下标的cell为空,需要创建
//3.说明cas失败,意味着当前线程对应的cell有竞争[重试 or 扩容cells]
//wasUncontended: 只有cells初始化之后,并且当前线程竞争修改失败,才会是false
final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
//h: 表示线程的hash值
int h;
//条件成立: 说明当前线程还未分配hash值
if ((h = getProbe()) == 0) {
//1.给当前线程分配hash值
ThreadLocalRandom.current(); // force initialization
//2.提取当前线程的hash值
h = getProbe();
//3.由于默认情况下,当前线程肯定写入到cells[0]位置上,完成步骤2后,会重新分配cells数组的位置给当前线程写入。因此,不当做一次竞争。
wasUncontended = true;
}
//表示扩容意向 false:一定不会扩容 true:可能会扩容
boolean collide = false; // True if last slot nonempty
//for (;;) 表示自旋
for (;;) {
//cells: volatile类型的数组,当其它线程修改时,会同步更新到主存
//as: 引用cells数组
//a: 当前线程命中的cell
//n: cells数组长度
//v: 期望值
Cell[] as; Cell a; int n; long v;
//前置条件:当前线程对应下标的cell为空,需要创建 or 当前线程对应的cell有竞争
//条件1:cells数组已经初始化了,当前线程将数据写入到对应的cell中
if ((as = cells) != null && (n = as.length) > 0) {
//条件1.1 true -> 表示当前线程对应下标位置的cell为null,需要new cell
if ((a = as[(n - 1) & h]) == null) {
//true -> 表示当前锁未被占用 false -> 表示当前锁被占用
if (cellsBusy == 0) { // Try to attach new Cell
//拿需要计算的值创建cell
Cell r = new Cell(x); // Optimistically create
//条件1.1.1 true -> 表示当前锁未被占用 false -> 表示当前锁被占用
//条件1.1.2 true -> 表示当前线程获取锁成功 false -> 表示当前线程获取锁失败
if (cellsBusy == 0 && casCellsBusy()) {
//是否创建成功 标记
boolean created = false;
try { // Recheck under lock
//rs 表示当前cells数组
//m 表示当前cells数组的长度
//j 表示当前线程命中的下标
Cell[] rs; int m, j;
//条件1、2恒成立 cells数组不为空
//条件3 rs[j = (m - 1) & h] == null 再次判断当前线程要赋值的cell是否已经被其它线程提前赋值了
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,因为cells数组中还有cell为创建
collide = false;
}
//条件1.2 当前线程竞争修改cell失败
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//条件1.3 当前线程的hash值进行1次rehash之后,新命中的cell不为null
// true -> 写成功,退出自旋
// false -> 表示rehash之后新命中的cell也有竞争 重试1次
else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
break;
//条件1.4:判断是否可扩容
// 条件1.4.1:n >= NCPU
// true -> cells数组长度已经 >= cpu核数,不可进行扩容,把扩容意向改为false
// false -> 可扩容
// 条件1.4.2:cells != as
// true -> 其它线程已经扩容过了,当前线程rehash之后重试即可
// false -> 未有线程对cells进行修改
else if (n >= NCPU || cells != as)
//把扩容意向改为false
collide = false; // At max size or stale
//条件1.5:设置为扩容后,下次再执行到这里就将会进行扩容操作
// 条件1.5.1:!collide
// true -> 设置扩容意向为true,但不一定真的扩容
else if (!collide)
collide = true;
//条件1.6:真正的扩容逻辑
// 条件1.6.1:cellsBusy == 0
// true -> 表示cells没有被其它线程占用,当前线程可以去竞争锁
// false -> 表示有其它线程正在操作cells
// 条件1.6.2:casCellsBusy()
// true -> 表示当前线程获取锁成功,可以进行扩容操作
// false -> 表示当前线程获取锁失败,当前时刻有其它线程在做扩容相关的操作
else if (cellsBusy == 0 && casCellsBusy()) {
try {
//重复判断一下当前线程的临时cells数组是否与原cells数组一致(防止有其它线程提前修改了cells数组,因为cells是volatile的全局变量)
if (cells == as) { // Expand table unless stale
//n << 1 表示数组长度翻一倍
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
//扩容后,将扩容意向置为false
collide = false;
continue; // Retry with expanded table
}
//重置当前线程hash值
h = advanceProbe(h);
}
//条件2:前置条件:cells数组未初始化 as 为 null
// 条件2.1:判断锁是否被占用
// true -> 表示当前未加锁
// false -> 表示当前已加锁
// 条件2.2:因为其它线程可能会在当前线程给as赋值之后修改了cells
// true -> cells没有被其它线程修改
// false -> cells已经被其它线程修改
// 条件2.3:获取锁
// true -> 获取锁成功 会把cellsBusy = 1
// false -> 表示其它线程正在持有这把锁
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;
}
//条件3:将数据累加到base中
// 前置条件:1.cellsBusy被加了锁,表示其它线程在初始化cells,所以当前线程值累加到base
// 2.cells被其它线程初始化后,当前线程值累加到base (as == null 但 cells != as的情况)
else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}