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 (
cmpxchgon 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
synchronizedin 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
balancethat requires checking before updating,AtomicIntegeralone 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
AtomicIntegeris fast, but it’s not always a replacement forsynchronized.- It works best for single-value atomic updates.
- For complex operations, you still need locks.