14

Is there any scenario in which AtomicInteger.accumulateAndGet() can't be replaced with AtomicInteger.updateAndGet(), or is it just a convenience for method references?

Here's a simple example where I don't see any functional difference:

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

Obviously, the same goes for getAndUpdate() and getAndAccumulate().

2
  • 2
    Isn’t the ability to (re)use existing functions (or non-capturing method references) a useful feature? There’s also no functional difference between i.incrementAndGet() and i.accumulateAndGet(1, Integer::sum) Commented Mar 15, 2016 at 22:09
  • @Holger accumulateAndGet() was added later, plus it can't use Unsafe. addAndGet(1) might be a better example, but even that wasn't using Unsafe at the time it was created. I do, however, accept your general point; this question is just to clarify whether that was indeed the motivation. Commented Mar 15, 2016 at 22:25

2 Answers 2

10

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 
Sign up to request clarification or add additional context in comments.

Comments

4

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.