"private static final VarHandle PENDING" 在 CountedCompleter class 中的作用是什么

What's the effect of "private static final VarHandle PENDING" in CountedCompleter class

我正在阅读JDK9中CountedCompleter的源代码,这里是与我的问题相关的代码:

public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
    /** The number of pending tasks until completion */
    volatile int pending;

    // VarHandle mechanics
    private static final VarHandle PENDING;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);

        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * Sets the pending count to the given value.
     *
     * @param count the count
     */
    public final void setPendingCount(int count) {
        pending = count;
    }

    /**
     * Adds (atomically) the given value to the pending count.
     *
     * @param delta the value to add
     */
    public final void addToPendingCount(int delta) {
        PENDING.getAndAdd(this, delta);
    }

    /**
     * If the pending count is nonzero, (atomically) decrements it.
     *
     * @return the initial (undecremented) pending count holding on entry
     * to this method
     */
    public final int decrementPendingCountUnlessZero() {
        int c;
        do {} while ((c = pending) != 0 &&
                     !PENDING.weakCompareAndSet(this, c, c - 1));
        return c;
    }
}

这是我的问题:

  1. 为什么要用 pending 和 PENDING?为什么不直接使用 AtomicInteger?
  2. 为什么有时候用pending,比如setPendingCount(),有时候用PENDING,比如addToPendingCount()。它甚至同时使用两者,例如 decrementPendingCountUnlessZero()

Why it uses pending and PENDING?

前者是字段,后者是字段的句柄,对字段进行原子操作。

why not just use something like AtomicInteger?

他们可以。我怀疑这只是性能问题。并发性的大部分 API 都进行了高度优化,因为它们很可能涉及紧密循环。

AtomicInteger 在幕后所做的事情与此处所做的基本相同,以确保原子操作,因此使用它将是方法调用的一个间接级别,仅需少量复制粘贴即可.绝对微小的性能提升,是的,但对于一些专门的应用程序来说,这是值得的。

是的,重用 AtomicInteger 会是更简洁的代码,但并发 API 的目标与您或我的项目的目标不同。性能是第一位的,其他都是次要的。

Why sometimes it uses pending, for example in setPendingCount(), but sometimes it uses PENDING

它直接对已经是原子的操作使用volatile 字段。 setPendingCount 只是分配给字段

public final void setPendingCount(int count) {
    pending = count;
}

其他需要比较和设置的情况需要作为原子操作运行,VarHandle 提供该功能

public final boolean compareAndSetPendingCount(int expected, int count) {
    return PENDING.compareAndSet(this, expected, count);
}

And it even uses both, for example in decrementPendingCountUnlessZero()

同样,答案是原子性。他们用这种方法编写它的方式是实现该函数原子性的最有效方式。