Practical uses for AtomicInteger
There are two main uses of AtomicInteger
:
As an atomic counter (
incrementAndGet()
, etc) that can be used by many threads concurrentlyAs a primitive that supports compare-and-swap instruction (
compareAndSet()
) to implement non-blocking algorithms.Here is an example of non-blocking random number generator from Brian Göetz's Java Concurrency In Practice:
public class AtomicPseudoRandom extends PseudoRandom { private AtomicInteger seed; AtomicPseudoRandom(int seed) { this.seed = new AtomicInteger(seed); } public int nextInt(int n) { while (true) { int s = seed.get(); int nextSeed = calculateNext(s); if (seed.compareAndSet(s, nextSeed)) { int remainder = s % n; return remainder > 0 ? remainder : remainder + n; } } } ... }
As you can see, it basically works almost the same way as
incrementAndGet()
, but performs arbitrary calculation (calculateNext()
) instead of increment (and processes the result before return).
The absolute simplest example I can think of is to make incrementing an atomic operation.
With standard ints:
private volatile int counter;
public int getNextUniqueIndex() {
return counter++; // Not atomic, multiple threads could get the same result
}
With AtomicInteger:
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
The latter is a very simple way to perform simple mutations effects (especially counting, or unique-indexing), without having to resort to synchronizing all access.
More complex synchronization-free logic can be employed by using compareAndSet()
as a type of optimistic locking - get the current value, compute result based on this, set this result iff value is still the input used to do the calculation, else start again - but the counting examples are very useful, and I'll often use AtomicIntegers
for counting and VM-wide unique generators if there's any hint of multiple threads being involved, because they're so easy to work with I'd almost consider it premature optimisation to use plain ints
.
While you can almost always achieve the same synchronization guarantees with ints
and appropriate synchronized
declarations, the beauty of AtomicInteger
is that the thread-safety is built into the actual object itself, rather than you needing to worry about the possible interleavings, and monitors held, of every method that happens to access the int
value. It's much harder to accidentally violate threadsafety when calling getAndIncrement()
than when returning i++
and remembering (or not) to acquire the correct set of monitors beforehand.