LongAdder Striped64 wasUncontended 实现细节

LongAdder Striped64 wasUncontended implementation detail

这不是关于 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 方法并且busy spin 处于第一个周期。

这就像这段代码在说:如果你(某个线程)以前来过这里并且你对特定的 Cell 槽有一些争用,不要尝试将你的值 CAS 到已经存在的值,而是重新散列探测器。

我真诚地希望我能对某些人有所帮助。

不是会失败,而是失败了。这个方法的调用是通过LongAdderadd方法完成的。

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);
    }
}
  1. 第一组条件句与长单元格的存在有关。如果必要的单元格不存在,那么它将尝试通过原子地添加必要的单元格然后添加来积累无竞争的(因为没有尝试添加)。
  2. 如果单元格确实存在,请尝试添加 (v + x)。如果添加失败则存在某种形式的争用,在这种情况下尝试进行累加 optimistically/atomically(旋转直到成功)

那么为什么它有

wasUncontended = true;      // Continue after rehash

我最好的猜测是,如果竞争激烈,它将尝试给 运行 线程时间来赶上进度,并强制重试现有单元格。