Detailed explanation
Atomic classes in java.util.concurrent.atomic, such as AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference, support thread-safe updates to individual variables without requiring an explicit synchronized block. They are useful for shared counters, state flags, sequence generators, and conditional state transitions. The important guarantee is atomicity: another thread cannot observe or interfere with an update halfway through. For example, counter.incrementAndGet() performs one indivisible increment and returns the updated value, even when multiple threads increment the same counter concurrently.
A normal int increment, including an increment of a volatile int, is a read-modify-write sequence. Two threads can read the same value, compute the same next value, and overwrite each other's updates. volatile provides visibility and ordering guarantees, but does not make that entire sequence atomic. AtomicInteger combines atomic updates with memory-visibility guarantees. Its get() and set() have volatile-style memory effects, while methods such as incrementAndGet(), getAndIncrement(), and compareAndSet() provide atomic read-modify-write behaviour. These guarantees let threads coordinate on one value without protecting every access with a monitor lock.
A central mechanism is compare-and-swap, exposed as compareAndSet(expected, update). It changes the value only if the current value equals expected, returning true on success and false otherwise. With AtomicInteger counter = new AtomicInteger(0), incrementAndGet() changes the value to 1; compareAndSet(1, 5) then changes it to 5 if no intervening update changes that value. JVM implementations typically use processor-supported atomic instructions. Some update operations use CAS retry loops, while others may use direct atomic instructions. Lock-free does not mean every thread finishes immediately: contention can cause repeated retries, and individual threads may be delayed.
Interviewers usually test whether you understand the boundary of this guarantee. Atomic methods protect individual operations, not arbitrary multi-step logic or invariants across several variables. Checking counter.get() > 0 and then decrementing is not one atomic action; use a suitable CAS loop or locking. Atomic classes often suit simple shared state, but they are not automatically faster than synchronization. For highly contended statistical counters, LongAdder may offer better throughput, although its sum is not an atomic snapshot during concurrent updates.
Key points to remember
- Atomic classes provide atomic updates and visibility guarantees for individual shared variables.
- volatile int does not make increment operations atomic; AtomicInteger.incrementAndGet() does.
- compareAndSet(expected, update) conditionally updates the value and reports success as a boolean.
- Lock-free operations avoid monitor locking, but contention can still cause retries and delays.
- Use locks or a carefully designed atomic state representation when several values must change consistently.
Common mistakes candidates make
- Assuming volatile makes compound operations such as count++ thread-safe.
- Treating a sequence of atomic method calls as one atomic transaction.
- Claiming atomic classes are always faster than synchronized or that lock-free means wait-free.
Likely follow-up questions
What is the difference between incrementAndGet() and getAndIncrement()?
Both atomically increment the value by one. incrementAndGet() returns the new value, whereas getAndIncrement() returns the value before the increment.
How can you decrement an AtomicInteger only when its value is positive?
Use a loop that reads the current value, returns without decrementing if it is non-positive, and otherwise attempts compareAndSet(current, current - 1). If CAS fails, reread and retry because another thread may have changed the value.
When would you choose LongAdder instead of AtomicLong?
LongAdder is useful for frequently updated statistical counters under high contention because it spreads updates across internal variables. Choose AtomicLong when you need operations such as compareAndSet() or an atomic read of the current total.
Interview tip: Explain the lost-update problem first, demonstrate incrementAndGet() and compareAndSet(), and finish by stating that atomic methods do not automatically protect multi-step logic.
