Reputation: 511
AtomicInteger.incrementAndGet() is atomic according to the documentation.However, in its source code below, what if another thread interleaves just before "return next"? "Next" will be incorrect then?
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
Upvotes: 7
Views: 1605
Reputation: 5120
What is "correct"?
The next value is not the current value of AtomicInteger but the value resulted by increment the "current" value.
Let's suppose the following simulation (in such way, "thread 2" interleaves "thread 1" just before "return next")
i = 10
Thread 1
calling 'j = incrementAndGet(i)'
computing incrementAndGet(i) by Thread 1
now i = 11
Thread 2
calling 'm = incrementAndGet(i)'
computing incrementAndGet(i) by Thread 2
now i = 12
Thread 1
now j = 11
Thread 2
now m = 12
Thread 3
calling 'n = incrementAndGet(i)'
// Hey! It should be only with Thread 1 and 2!
now i = 13 and n = 13
Indeed, incrementAndGet was atomic. Why? Because Thread 1 called incrementAndGet when i was 10 and get 11. And Thread 2 called incrementAndGet when i was 11 and get 12. It doesn't matter if i will be changed. After incrementAndGet, the caller get the resulting value after the increment.
Upvotes: 2
Reputation: 338
The incrementAndGet is using compareAndSet and which is using unsafe.compareAndSwapInt. This method Atomically update Java variable to given integer if it is currently holding the expected integer.
So when two thread tries to increment the value then JVM will atomically compare existing value if it is same then increment will be successful else it will not be successful.
Upvotes: 1
Reputation: 65793
If another thread interleaved then it would also successfully add one to the value.
Atomicity guarantees that your increment happens and that if two threads attempt an increment then eventually two threads will succeed (and increment by two). It does not guarantee anything about any future value.
In your scenario it would seem like the following code:
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next)) {
// Other thread.
for (;;) {
long current2 = get();
long next2 = current2 + 1;
if (compareAndSet(current2, next2)) {
return next2;
}
}
return next;
}
}
}
i.e. each thread gets it's own incremented value.
Upvotes: 6
Reputation: 108939
The compareAndSet
method guarantees the atomicity of the update, however there is no guarantee that the value returned is the 'latest'. The incrementAndGet
only guarantees that 1) the value was incremented atomically and 2) that you get the result of that increment. What happened before (or after) the successful increment is of no concern to the current Thread
.
Upvotes: 2
Reputation: 13608
The value is incremented with every call correctly, so the only thing it could affect is the return value.
The semantics of the function are that it doesn't return the current value of the integer, but the value to which calling incrementAndGet()
increased the integer (the updated value).
Let me give you an example:
public MyClass {
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
}
Now if you call getNextUniqueIndex()
it will always return a unique value regardless of the calling thread or thread interleaving. That is because the value of next
returned from the implementation of incrementAndGet()
refers to the value updated by this call of incrementAndGet()
.
Upvotes: 3
Reputation: 109547
If after compareAndSet
then yes, incrementAndGet might have delivered a higher value to some other thread. However the same would hold after returning. The value would have increased by two (correct), and both threads would have received a different value.
This forms "correct" semantics. With parallel threads there is no perfect time synchronicity.
Upvotes: 2