Java Concurrency: volatile vs synchronized vs Atomic vs Locks (2025 Guide)
A complete comparison for backend engineers & interviews.
Java provides multiple tools to handle concurrency:
✅ volatile
✅ synchronized
✅ java.util.concurrent.atomic (e.g., AtomicInteger)
✅ Locks (e.g., ReentrantLock)
Each solves different problems.
✅ 1. Summary Comparison Table
| Feature | volatile | synchronized | Atomic Classes | Locks (ReentrantLock) |
|---|---|---|---|---|
| Visibility | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Atomicity | ❌ No | ✅ Yes | ✅ Yes (for built-in ops) | ✅ Yes |
| Prevents Reordering | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Allows Multiple Threads? | ✅ Yes | ✅ Only inside critical section | ✅ Yes | ✅ Yes |
| Performance | ⭐ Very Fast | ⚠️ Slower (blocking) | ⭐ Fast (non-blocking) | ⭐ Fast (flexible, non-blocking options) |
| Blocking | ❌ No | ✅ Yes | ❌ No | ⚠️ Optional locking |
| Use Case | State flags, simple booleans | Multi-step operations | Counters, accumulators | Complex concurrency control |
✅ 2. What Each One Actually Does
✅ A. volatile
What it is:
A lightweight keyword ensuring visibility and ordering, but not atomicity.
Use when:
-
Shared flags
-
Configuration states
-
“Stop thread” signals
-
Values written by one thread and read by many threads
-
No dependency on old value
Not good for:
❌ counters (i++), ❌ collections, ❌ multi-step updates
✅ B. synchronized
What it is:
A monitor lock that ensures:
✅ Only one thread can enter a block/method
✅ Atomicity
✅ Visibility
✅ Ordering
Use when:
-
Multi-step operations
-
Updating shared objects
-
Consistency required across multiple variables
Drawbacks:
⚠️ Blocking → thread waits
⚠️ Can cause contention
⚠️ Can lead to deadlock if misused
✅ C. Atomic Classes (AtomicInteger, AtomicLong, AtomicReference…)
What they are:
Non-blocking, lock-free concurrency utilities using CAS (Compare-And-Set) operations.
Use when:
-
High-performance counters
-
Increment/decrement
-
Accumulators
-
Single-variable atomic operations
Benefits:
✅ Faster than synchronized
✅ No blocking
✅ Thread-safe increments
Limitations:
❌ Not good for multi-step logic
❌ Limited to atomic ops on one variable
✅ D. Locks (ReentrantLock, ReentrantReadWriteLock…)
What they are:
Advanced locking primitives offering more control than synchronized.
Use when:
-
Need timed lock attempts
-
Need interruptible locks
-
Need fairness policies
-
Need read/write locks
-
Need conditions (like wait/notify but better)
Benefits:
✅ More flexible
✅ Can try to acquire lock without waiting
✅ ReadWriteLock allows multiple readers
Drawbacks:
⚠️ Must manually unlock (error-prone)
⚠️ More complex to write
⚠️ Slight overhead vs synchronized
✅ 3. Code Examples for Each
✅ volatile Example — simple visibility
✅ Safe because it's a simple boolean
❌ Not safe for increments
✅ synchronized Example — atomic multi-step
✅ Guarantees atomicity
✅ Prevents race conditions
✅ AtomicInteger Example — high performance counter
✅ Non-blocking
✅ Fast
✅ Thread-safe
✅ ReentrantLock Example — flexible locking
✅ Supports tryLock(), fairness, conditions
✅ Good for complex concurrency
✅ 4. When to Use What (Interview Gold Answer)
| Scenario | Best Choice | Why |
|---|---|---|
| Boolean flags, configuration, “stop threads” | volatile | Lightweight & visible |
| Multi-step updates | synchronized | Atomic + blocking |
| High-performance counters (increment/decrement) | AtomicInteger | Non-blocking, fast |
| Multiple readers, one writer | ReadWriteLock | Better concurrency |
| Complex lock control (timeout, interrupt, fairness) | ReentrantLock | More flexible |
| Protecting compound operations across many fields | synchronized or ReentrantLock | Coordinated atomicity |
| Replacing synchronized for performance | Atomic* or ReentrantLock | Lock-free, fine-grained |
✅ 5. Memory Model Diagram (Visibility Problem)
✅ 6. Simple Decision Tree
No comments:
Post a Comment