TommyQ
TommyQ

Reputation: 511

AtomicInteger incrementAndGet atomicity

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

Answers (6)

rdllopes
rdllopes

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

Hemang Rami
Hemang Rami

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

OldCurmudgeon
OldCurmudgeon

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

Mark Rotteveel
Mark Rotteveel

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

Mifeet
Mifeet

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

Joop Eggen
Joop Eggen

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

Related Questions