AtomicInteger vs synchronized — When to Use Each
Fast recall
Quick revision
Core answer
Understanding of lock-free programming fundamentals and when CAS-based classes provide a real performance benefit.
Assuming AtomicInteger is always faster — under very high contention, CAS spin-retry can waste more CPU than a fair lock, and LongAdder is the better choice for pure incrementing.
Mention ABA problem in CAS, the existence of AtomicStampedReference to mitigate it, and LongAdder as the contention-optimised counter.
Interview-ready guide
Interview answer
AtomicInteger uses CAS — a CPU instruction that atomically reads, compares, and writes without acquiring a lock.
CAS mechanics:
- Atomically reads current value, checks if it equals expected, writes new value only if it matches — else retries in a spin loop
- Under low contention: nanoseconds, much faster than acquiring a monitor lock
- Under high contention: spin loop burns CPU; cache-line bouncing between cores makes it worse — synchronized can outperform
LongAdder (Java 8):
- Distributes increments across an array of cells — each thread writes to its own cell, avoiding cache-line bouncing
sum()aggregates but is not an atomic snapshot — correct for statistics, not for exact coordination- Better than
AtomicLongunder high contention scenarios
CAS limitation:
- Works on one variable at a time — to update two related fields atomically, use
synchronizedorReentrantLock
I recommend AtomicInteger for single-variable counters under moderate contention, LongAdder for high-contention statistics counters, and synchronized when multiple variables must change together.
AtomicInteger vs synchronized — When to Use Each
Direct Answer
AtomicInteger uses hardware-level compare-and-swap (CAS) to perform single-variable updates without a lock, making it faster under low-to-medium contention for single-variable operations. synchronized (or explicit locks) are necessary for multi-variable invariants or compound operations that span several fields. For high-contention counters, LongAdder can outperform both by maintaining per-CPU cells.
Deep Dive & Architecture
Synchronization is how Java serializes access to shared mutable state so that concurrent code stays correct.
It provides two guarantees: mutual exclusion (only one thread executes the protected section at a time) and memory visibility (writes made inside a synchronized block by one thread are visible to any thread that later acquires the same lock).
AtomicInteger uses hardware-level compare-and-swap (CAS) to perform single-variable updates without a lock, making it faster under low-to-medium contention for single-variable operations. synchronized (or explicit locks) are necessary for multi-variable invariants or compound operations that span several fields. For high-contention counters, LongAdder can outperform both by maintaining per-CPU cells.
Mentioning only mutual exclusion. Missing the visibility / memory-model angle entirely. Confusing volatile (visibility only) with synchronized (both)
Common Mistakes & Pitfalls
Assuming AtomicInteger is always faster — under very high contention, CAS spin-retry can waste more CPU than a fair lock, and LongAdder is the better choice for pure incrementing.
Check your understanding
Answer these without looking back at the article.
- What is synchronization in Java? Why is it needed, and what guarantees does it provide?
- Explain how the synchronized keyword works in Java. What is a monitor, and what guarantees does synchronization provide?
- What's the difference between a synchronized method and a synchronized block in Java? When should you use each?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.