Is there any functional difference between AtomicInteger.updateAndGet() and AtomicInteger.accumulateAndGet()?
When in doubt, you may look into implementation:
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;
}
They differ only in single line and obviously accumulateAndGet
could be expressed easily via updateAndGet
:
public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) {
return updateAndGet(prev -> accumulatorFunction.applyAsInt(prev, x));
}
So updateAndGet
is somewhat more basic operation and accumulateAndGet
is a useful shortcut. Such shortcut might be especially helpful if your x
is not effectively final:
int nextValue = 5;
if(something) nextValue = 6;
i.accumulateAndGet(nextValue, Math::max);
// i.updateAndGet(prev -> Math.max(prev, nextValue)); -- will not work
There are cases where an instance creation can be avoided by using accumulateAndGet
.
This is not really a functional difference but it might be useful to know about.
Consider the following example:
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 cached in a field, or maybe the optimizer is able
// to reuse it automatically.
IntBinaryOperator accumulatorFunction =
(incValueParam, value) -> incValueParam + value;
i.accumulateAndGet(incValue, accumulatorFunction);
}
Instance creations are generally not expensive but can be important to get rid of in short operations that are used very frequently in performance sensitive locations.
More information about when lambda instances are reused can be found in this answer.