65

When return value is not of interest, is there any (even irrelevant in practice) difference between AtomicInteger.getAndIncrement() and AtomicInteger.incrementAndGet() methods, when return value is ignored?

I'm thinking of differences like which would be more idiomatic, as well as which would put less load in CPU caches getting synchronized, or anything else really, anything to help decide which one to use more rationally than tossing a coin.

4
  • 1
    if return value is of interest, is it possible that in the case of AtomicInteger.getAndIncrement() two threads will get the same value? Commented Jul 18, 2018 at 12:42
  • @FaizHalde: that should have been asked in a separate question. But no, if nothing decrements the atomic integer (and it doesn't overflow), then no two calls will observe the same value from calling getAndIncrement. Commented Aug 31, 2020 at 8:19
  • @FaizHalde if 2 or more threads are in contention, "prev" or in this case "current" will be the same... but after a compareAndSet() only one thread (the winner) will return. This winner will return the version that matched the version that the thread saw at the beginning of the spin. Commented Feb 25, 2022 at 19:55
  • @FaizHalde ... All threads that are still competing will fail their comapreAndSet() because the version of the atomic value will no longer match the one they saw when the first .get() at the beginning of their respective spin-locks was made, and so they will retry, on their next spin their "current" will finally reflect the change, so the answer is: No, AtomicInteger.getAndIncrement() will never bring the same value even under heavy contention. Commented Feb 25, 2022 at 19:58

6 Answers 6

45

Since no answer to the actual question has been given, here's my personal opinion based on the other answers (thanks, upvoted) and Java convention:

incrementAndGet() 

is better, because method names should start with the verb describing the action, and intended action here is to increment only.

Starting with verb is the common Java convention, also described by official docs:

"Methods should be verbs, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized."

Sign up to request clarification or add additional context in comments.

2 Comments

Would be nice if they exposed a simple increment() method, just for the sake of readability.
I guess the reason why they kept it like that in atomicInteger() is to "maintain the convention" as the behaviour of returning either "prev" or "next" becomes a necessity when dealing with atomic swaps. This is not useful in integers since the next will always be prev + 1 (and prev == next -1), but when dealing with objects it is extremely useful. More problematic to me is the fact that a failed compareAndSet does not give you a witness.
37

The code is essentially the same so it does not matter:

public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } 

6 Comments

incrementAndGet()==getAndIncrement()+1
@irreputable Not sure I get your point - if you don't store the returned value, var.incrementAndGet(); and var.getAndIncrement(); produce exactly the same result...
yes. I'm saying actually one of the methods is enough, the other is kind of redundant.
@irreputable Ah sorry... Yes. Also I'm not sure why incrementAndGet does not simply call addAndGet(1) instead of mostly duplicating the code...
Doug Lea is obsessed with every CPU cycle:)
|
11

No, there's no difference (if you don't care about the return value).

The code of those methods (in the OpenJDK) differs only in that one uses return next and the other uses return current.

Both use compareAndSet under the hood with the exact same algorithm. Both need to know both the old and the new value.

Comments

9

Just want to add to existing answers: there could be very small non-noticeable difference.

If you look at this implementation:

public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } 

Note - both function call exactly the same function getAndAddInt, except +1 part, which means that in this implementation getAndIncrement is faster.

But, here is older implementation:

public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } 

The only difference is return variable, so both functions perform exactly the same.

Comments

1

All else equal, prefer getAndIncrement()

Some CPUs support it directly (even when the return address is used), unlike incrementAndGet(), so you might save one inc reg instruction if the return value is used.


The portable CAS retry loop isn't the implementation actually used on real JVMs for mainstream ISAs that have instructions for this. At least I hope not!

If your JVM is any good, these will compile + JIT to a single instruction on x86 or ARMv8.1.

If the return value isn't used, either can be x86 lock inc dword [mem]

If the return value is used (or the optimizer doesn't know this peephole), getAndIncrement() is the same operation as C++ fetch_add(1), which is x86 lock xadd [mem], reg (exchange-and-add) or ARMv8.1 ldadd (or ldaddal for the seq_cst version).

There is no single-instruction IncAndGet; you need a separate add or inc reg if you want that. But if you don't use the return value, hopefully a compiler can still omit that even if it uses lock xadd instead of

For example in C:

#include <stdatomic.h> void Inc(_Atomic int *p) { ++*p; } int GetAndInc(_Atomic int *p) { //return atomic_fetch_add_explicit(p, 1, memory_order_relaxed); return (*p)++; // uses seq_cst like Java is stuck with } int IncAndGet(_Atomic int *p) { //return 1 + atomic_fetch_add_explicit(p, 1, memory_order_relaxed); return ++*p; } 

AArch64 assembly (from Godbolt, Clang 19 -O2 -mcpu=cortex-a77)

Inc: mov w8, #1 ldaddal w8, w8, [x0] // Load+ADD Acquire reLease ret GetAndInc: mov w8, #1 ldaddal w8, w0, [x0] ret IncAndGet: mov w8, #1 ldaddal w8, w8, [x0] add w0, w8, #1 // turn fetch_add result into add_fetch ret 

Same for x86-64

Inc: lock inc dword ptr [rdi] ret GetAndInc: mov eax, 1 lock xadd dword ptr [rdi], eax ret IncAndGet: mov eax, 1 lock xadd dword ptr [rdi], eax inc eax ret 

On ISAs without single-instruction RMWs, like ARMv7 and ARMv8.0, or MIPS or PowerPC or I think RISC-V, there's no difference, or if anything incrementAndGet needs fewer spare registers since it can overwrite the load result with the add result. Same number of instructions either way for ARMv8.0 when told to inline the old-style instructions instead of calling libatomic helper functions in the hope that ARMv8.1 instructions will be available at run-time even if we don't want to require them (-mno-outline-atomics = inline atomics). These functions aren't running short on registers, only limited by needing the return value in the same register as the incoming pointer.

# AArch64 clang -O2 -Wall -mcpu=cortex-a53 -mno-outline-atomics GetAndInc: .LBB1_1: ldaxr w8, [x0] // load-acquire exclusive add w9, w8, #1 // add into a different register stlxr w10, w9, [x0] // store-release exclusive cbnz w10, .LBB1_1 mov w0, w8 ret IncAndGet: .LBB2_1: ldaxr w8, [x0] add w8, w8, #1 // overwrite the load result stlxr w9, w8, [x0] cbnz w9, .LBB2_1 mov w0, w8 ret 

My recommendation at the top of the answer to favour getAndIncrement() is based on the fact that if an ISA provides a single-instruction atomic, it's normally a fetch_add not an add_fetch. And that is the case on the two most common mainstream ISAs, x86-64 and AArch64.


as well as which would put less load in CPU caches getting synchronized

Absolutely no difference. The atomic RMW is the same either way, it's just a question of whether you need to fix-up the result for local use.


Fun fact: GNU C __atomic builtins also come in both flavours, __atomic_fetch_add and __atomic_add_fetch. (https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html#index-_005f_005fatomic_005fadd_005ffetch).

Comments

0

Here I am giving an example. Hope it will clear your doubt.

Suppose I have a variable i as

AtomicInteger i = new AtomicInteger();

In this case:

i.getAndIncrement() <==> i++;

And

i.incrementAndGet() <==> ++i;

Please have a look of the below programs

public class Test1 { public static void main(String[] args) { AtomicInteger i = new AtomicInteger(); System.out.println(i.incrementAndGet()); System.out.println(i); } } 

**output

1 1 ======================================**

public class Test2 { public static void main(String[] args) { AtomicInteger i = new AtomicInteger(); System.out.println(i.getAndIncrement()); System.out.println(i); } } 

**output

0 1 -------------**

Comment: 1) In the class Test1, incrementAndGet() will first increment the i value and then print.

2) In the class Test2, getAndIncrement() will first print the i value and then increment.

That's all.

1 Comment

Thanks for your answer. However, in your answer, the result is actually used (printed). The question is about a situation where "When return value is not of interest"...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.