Есть ли функциональная разница между AtomicInteger.updateAndGet () и AtomicInteger.accumulateAndGet ()?

есть ли какой-либо сценарий, в котором AtomicInteger.accumulateAndGet() нельзя заменить на AtomicInteger.updateAndGet(), или это просто удобство для ссылки на метод?

вот простой пример, где я не вижу функциональной разницы:

AtomicInteger i = new AtomicInteger();
i.accumulateAndGet(5, Math::max);
i.updateAndGet(x -> Math.max(x, 5));

очевидно, то же самое касается getAndUpdate() и getAndAccumulate().

2 ответов


когда вы сомневаетесь, вы можете посмотреть в реализация:

public final int accumulateAndGet(int x,
                                  IntBinaryOperator accumulatorFunction) {
    int prev, next;
    do {
        prev = get();
        next = accumulatorFunction.applyAsInt(prev, x);
    } while (!compareAndSet(prev, next));
    return next;
}

public final int updateAndGet(IntUnaryOperator updateFunction) {
    int prev, next;
    do {
        prev = get();
        next = updateFunction.applyAsInt(prev);
    } while (!compareAndSet(prev, next));
    return next;
}

они отличаются только одной строкой и, очевидно,accumulateAndGet может быть легко выражена через updateAndGet:

public final int accumulateAndGet(int x,
                                  IntBinaryOperator accumulatorFunction) {
    return updateAndGet(prev -> accumulatorFunction.applyAsInt(prev, x));
}

так updateAndGet несколько более базовая операция и accumulateAndGet - это полезно. Такой ярлык может быть особенно полезен, если ваш x не является фактически окончательным:

int nextValue = 5;
if(something) nextValue = 6;
i.accumulateAndGet(nextValue, Math::max);
// i.updateAndGet(prev -> Math.max(prev, nextValue)); -- will not work

есть случаи, когда создание экземпляра можно избежать, используя accumulateAndGet.

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

рассмотрим следующий пример:

void increment(int incValue, AtomicInteger i) {
    // The lambda is closed over incValue. Because of this the created
    // IntUnaryOperator will have a field which contains incValue. 
    // Because of this a new instance must be allocated on every call
    // to the increment method.
    i.updateAndGet(value -> incValue + value);

    // The lambda is not closed over anything. The same
    // IntBinaryOperator instance can be used on every call to the 
    // increment method.
    //
    // It can be cashed in a field, or maybe the optimizer is able 
    // to reuse it automatically.
    IntBinaryOperator accumulatorFunction =
            (incValueParam, value) -> incValueParam + value;

    i.accumulateAndGet(incValue, accumulatorFunction);
}

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

дополнительные сведения о том, когда экземпляры лямбда повторно используется можно найти в ответ.