Каков эффект "private stati c final VarHandle PENDING" в классе CountedCompleter - PullRequest
2 голосов
/ 09 июля 2020

Я читаю исходный код CountedCompleter в JDK9, вот код, связанный с моим вопросом:

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? почему бы просто не использовать что-то вроде AtomicInteger?
  2. Почему иногда используется отложенное выполнение, например, в setPendingCount(), а иногда PENDING, например, в addToPendingCount(). И даже использует оба, например, в decrementPendingCountUnlessZero()

1 Ответ

2 голосов
/ 09 июля 2020

Почему он использует pending и PENDING?

Первое - это поле, второе - его дескриптор для выполнения операций atomi c над полем.

почему бы просто не использовать что-то вроде AtomicInteger?

Они могли бы. Я подозреваю, что это просто производительность. Большинство частей API параллелизма сильно оптимизированы, так как с большой вероятностью они будут задействованы в узких циклах.

AtomicInteger под капотом делает в основном те же вещи, что и здесь, для обеспечения операций atomi c, поэтому его использование было бы одним из уровней косвенного обращения к вызовам методов, которое является избыточным с небольшим количеством копипаста. Абсолютно крошечный прирост производительности, да, но для некоторых специализированных приложений это того стоит.

Да, было бы проще повторно использовать AtomicInteger, но цели API параллелизма отличаются от те из ваших или моих проектов. Производительность - приоритет №1, а все остальное вторично.

Почему иногда используется pending, например, в setPendingCount(), а иногда PENDING

Он использует volatile поле непосредственно для операций, которые уже являются atomi c. setPendingCount просто назначает поле

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

В других случаях, когда требуется сравнение и установка, необходимо функционировать как операции atomi c, а VarHandle обеспечивает эту функциональность

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

И даже использует оба, например, в decrementPendingCountUnlessZero()

Опять же, ответ - атомарность. То, как они написали это в этом методе, - наиболее эффективный способ добиться атомарности для этой функции.

...