LongAdder Striped64是无对称的实现细节

时间:2016-12-13 13:05:00

标签: multithreading java-8 java.util.concurrent atomic-long

这个问题不是关于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次进入 longAccumulate 方法时(n> 1)和繁忙的旋转正是在它的第一个周期。

就像这段代码所说的那样:如果你(某些线程)曾经在这里,并且你在某个特定的Cell插槽上有争用,请不要尝试将你的价值CAS计算到现有的,而是重新调整探针。

老实说,我希望我会对某人有所了解。

1 个答案:

答案 0 :(得分:4)

不是它会失败,而是它失败了。对此方法的调用是通过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);
    }
}
  1. 第一组条件与长细胞的存在有关。如果不存在必要的单元格,那么它将尝试通过原子方式添加必要的单元格然后添加来无争议地累积(因为没有尝试添加)。
  2. 如果单元格确实存在,请尝试添加(v + x)。如果添加失败则存在某种形式的争用,在这种情况下尝试以乐观/原子方式进行累积(旋转直到成功)
  3. 那为什么呢

    wasUncontended = true;      // Continue after rehash
    

    我最好的猜测是,在争用激烈的情况下,它会尝试让正在运行的线程有时间赶上并强制重试现有单元格。