Java Concurrency Essentials: volatile, synchronized, and ThreadLocal
In Java, when multiple threads run at the same time, they often need to read or update shared data.
If not handled correctly, this leads to bugs that are hard to detect and reproduce.
Java provides three important mechanisms to handle this safely:
-
volatile -
synchronized -
ThreadLocal
Each solves a different concurrency problem.
🧠 The Core Problems in Multithreading
1️⃣ Visibility Problem
One thread updates a variable, but other threads do not see the updated value.
2️⃣ Race Condition
Multiple threads update the same variable at the same time, causing incorrect results.
3️⃣ Shared State Complexity
Threads compete for shared data, leading to:
-
locks
-
contention
-
performance issues
1️⃣ volatile Keyword
🔹 What Is volatile?
The volatile keyword ensures that changes made by one thread are immediately visible to other threads.
🔍 What volatile Guarantees
✅ Visibility
✅ Ordering (happens-before)
❌ Atomicity
❌ Mutual exclusion
❌ Problem Without volatile
🔴 The worker thread may never stop, because it keeps reading a cached value.
✅ Solution Using volatile
✔ All threads see the updated value immediately.
🏭 Real-World Use Cases of volatile
-
Shutdown flags
-
Feature toggles
-
Configuration refresh flags
-
Polling loops
⚠️ Important Limitation
Reason: count++ is a read–modify–write operation.
2️⃣ synchronized Keyword
🔹 What Is synchronized?
synchronized ensures that only one thread at a time can access a critical section of code.
It guarantees:
-
Mutual exclusion
-
Atomicity
-
Visibility
❌ Problem Without synchronized
Thread Code
Main Method
🔴 Output may be incorrect:
✅ Solution Using synchronized (Best Practice)
✔ Only one thread updates count at a time
✔ No race conditions
🏭 Real-World Use Case of synchronized
Bank Account Example
✔ Prevents double withdrawal
✔ Ensures data consistency
3️⃣ ThreadLocal
🔹 What Is ThreadLocal?
ThreadLocal provides one variable per thread, even though the variable appears shared.
Each thread:
-
has its own copy
-
cannot see other threads’ values
✅ Simple ThreadLocal Example
✅ Output
✔ Same variable
✔ Different value per thread
✔ No synchronization required
🏭 Real-World Use Cases of ThreadLocal
-
User context (userId, tenantId)
-
Request / trace ID in logging
-
Database connection per thread
-
DateFormat handling
-
Security context
⚠️ Important Warning
In thread pools, always clean up:
Otherwise → memory leaks.
🆚 Comparison Summary
| Feature | volatile | synchronized | ThreadLocal |
|---|---|---|---|
| Shared data | Yes | Yes | ❌ No |
| Visibility | ✅ | ✅ | N/A |
| Atomicity | ❌ | ✅ | ✅ (per thread) |
| Locking | ❌ | ✅ | ❌ |
| Performance | High | Lower | High |
| Main use | Flags | Counters | Context data |
🎯 When to Use What?
✔ Use volatile when:
-
One thread writes
-
Other threads read
-
Value is independent (flags)
✔ Use synchronized when:
-
Multiple threads update shared data
-
Atomicity and consistency are required
✔ Use ThreadLocal when:
-
Sharing can be avoided
-
Each thread needs its own state
📝 Interview-Ready One-Liner
volatileensures visibility,synchronizedensures mutual exclusion, andThreadLocalavoids shared state altogether.
🔚 Final Takeaway
Concurrency is not about choosing one keyword — it’s about choosing the right strategy.
-
Share safely →
volatileorsynchronized -
Don’t share →
ThreadLocal
No comments:
Post a Comment