Tuesday, July 11, 2023

What is AtomicInteger?

The primary use of AtomicInteger is when you are in a multithreaded context and you need to perform thread-safe operations on an integer without using synchronized. The assignation and retrieval of the primitive type int are already atomic but AtomicInteger comes with many operations which are not atomic on int.

The simplest is the getAndXXX or xXXAndGet. For instance getAndIncrement() is an atomic equivalent to i++ which is not atomic because it is actually a shortcut for three operations: retrieval, addition, and assignation. compareAndSet is very useful for implementing semaphores, locks, latches, etc.

Using the AtomicInteger is faster and more readable than performing the same using synchronization.

For example, I have a library that generates instances of some class. Each of these instances must have a unique integer ID, as these instances represent commands being sent to a server, and each command must have a unique ID. Since multiple threads are allowed to send commands concurrently, I use an AtomicInteger to generate those IDs. An alternative approach would be to use some sort of lock and a regular integer, but that's both slower and less elegant.


A simple test:

public synchronized int incrementNotAtomic() {
    return notAtomic++;
}

public void performTestNotAtomic() {
    final long start = System.currentTimeMillis();
    for (int i = 0 ; i < NUM ; i++) {
        incrementNotAtomic();
    }
    System.out.println("Not atomic: "+(System.currentTimeMillis() - start));
}

public void performTestAtomic() {
    final long start = System.currentTimeMillis();
    for (int i = 0 ; i < NUM ; i++) {
        atomic.getAndIncrement();
    }
    System.out.println("Atomic: "+(System.currentTimeMillis() - start));
}