这不是关于 LongAdder 如何工作的问题,而是关于一个我无法弄清楚的有趣的实现细节。
这是来自 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;
if ((as = cells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {
//logic to insert the Cell in the array
}
// CAS already known to fail
else if (!wasUncontended) {
wasUncontended = true; // Continue after rehash
}
else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))){
break;
}
代码中的很多东西对我来说都很清楚,除了:
// CAS already known to fail
else if (!wasUncontended) {
wasUncontended = true; // Continue after rehash
}
以下 CAS 将失败的确定性在哪里? 至少这对我来说真的很困惑,因为这种检查只对一种情况有意义:当某个线程第 n 次(n > 1)进入 longAccumulate 方法并且繁忙的旋转是在它的第一个周期。
这就像这段代码在说:如果你(某个线程)以前来过这里并且你对特定的 Cell 插槽有一些争用,不要尝试将你的值 CAS 到已经存在的值,而是重新散列探针.
老实说,我希望我能对某人有所帮助。
最佳答案
不是它会失败,而是它已经失败了。对该方法的调用是由 LongAdder
add
方法完成的。
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);
}
}
- 第一组条件与长单元格的存在有关。如果必要的单元格不存在,那么它将尝试通过原子地添加必要的单元格然后添加来积累无竞争的(因为没有尝试添加)。
- 如果该单元格确实存在,请尝试添加 (
v + x
)。如果添加失败,则存在某种形式的争用,在这种情况下,尝试乐观地/原子地进行累积(自旋直到成功)
那为什么会有
wasUncontended = true; // Continue after rehash
我最好的猜测是,在竞争激烈的情况下,它会尝试给正在运行的线程时间来 catch 进度,并强制重试现有单元格。
关于multithreading - LongAdder Striped64 wasUncontended 实现细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41121885/