How AtomicInteger
Works Under the Hood
AtomicInteger
is a thread-safe, lock-free alternative to int
that allows atomic operations without using synchronized
or explicit locks. It relies on low-level CPU instructions (CAS – Compare-And-Swap) to ensure atomicity.
1. Why Use AtomicInteger
?
In a multithreaded environment, updating a regular int
like this:
private int count = 0;
public void increment() {
count++; // Not thread-safe!
}
🚨 Not thread-safe!
- If multiple threads execute
count++
at the same time, they might read the same old value and overwrite each other’s updates. - This is a race condition.
✅ Instead, using AtomicInteger
ensures thread safety without locking:
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // Thread-safe
}
2. How AtomicInteger
Works Internally
Unlike synchronized
, which locks the entire method, AtomicInteger
uses lock-free atomic operations via Compare-And-Swap (CAS).
CAS (Compare-And-Swap) Mechanism
- Read the current value (
oldValue
). - Calculate the new value (
newValue
). - Use CAS to update the value only if no other thread changed it.
- If successful → update applied.
- If failed → retry until successful.
Key Internal Methods
A. incrementAndGet()
(Atomic Increment)
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
🔍 What happens under the hood?
- Uses
Unsafe.getAndAddInt()
, which performs a CAS operation to safely increment the value.
B. compareAndSet()
(CAS Operation)
public final boolean compareAndSet(int expected, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expected, update);
}
If expected == currentValue
, update it to update
.Otherwise, another thread has modified it, so retry.
3. Performance Advantage: Why No Locks?
Instead of synchronized
, AtomicInteger
relies on CPU instructions:
- CAS (
cmpxchg
on x86 CPUs) is executed in hardware, making it much faster than locks. - No context switching (which happens with locks).
- Reduces blocking and contention in multi-threaded environments.
🔹 Example: incrementAndGet()
vs. synchronized
Approach | Performance | Blocking? |
---|---|---|
synchronized | Slower | ✅ Yes |
AtomicInteger | Faster | ❌ No |
4. When Should You Use AtomicInteger
?
✅ Use AtomicInteger
when:
- You need simple atomic updates (counters, flags).
- You want better performance than
synchronized
in a concurrent environment. - Example: Thread-safe countersjavaCopyEdit
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet(); // Fast & thread-safe
}
❌ Don’t use AtomicInteger
when:
- You need compound operations (multiple related updates).
- Example: If you have a
balance
that requires checking before updating,AtomicInteger
alone is not enough.
🔹 For multiple related updates, use synchronized
or ReentrantLock
instead.
private int balance = 0; public synchronized void deposit(int amount) { if (amount > 0) { balance += amount; // Ensures atomicity } }
5. Summary
Feature | synchronized | AtomicInteger |
---|---|---|
Performance | Slower (uses locks) | Faster (lock-free) |
Thread Safety | ✅ Yes | ✅ Yes |
Blocking? | ✅ Yes (may cause thread contention) | ❌ No (uses CAS) |
Best For | Complex updates, multiple variables | Simple atomic operations |
Final Thoughts
AtomicInteger
is fast, but it’s not always a replacement forsynchronized
.- It works best for single-value atomic updates.
- For complex operations, you still need locks.