volatile guarantees visibility of a variable's latest value across threads but does not provide atomicity or mutual exclusion, so it cannot fully replace synchronization for anything beyond simple, single-variable read/write coordination.
Key Points: • volatile ensures every read sees the most recently written value by forcing reads and writes to bypass thread-local caching and go to main memory. • It does not make compound operations atomic — something like count++ is actually a read-modify-write, and volatile alone doesn't prevent two threads from interleaving those steps and losing an update. • volatile provides no mutual exclusion, so it can't protect a critical section spanning multiple related fields or steps. • It's appropriate for simple flags (e.g. a boolean "shutdown requested" flag checked in a loop) where only visibility, not atomicity, matters. • For atomic compound operations without full locking, java.util.concurrent.atomic classes like AtomicInteger are usually the better fit.
Example: A volatile boolean running flag checked in a worker thread's loop condition works fine because it's a simple visibility problem, but a volatile int counter incremented from multiple threads via counter++ can still lose updates, since the increment isn't atomic even though each individual read or write is visible.
Code Example:
private volatile boolean running = true;
public void stop() {
running = false; // visible to the worker thread promptly
}
public void run() {
while (running) {
// do work
}
}Interview Tip: A concise interview answer is:
"volatile only solves the visibility problem — it guarantees threads see the latest written value — but it doesn't provide atomicity or mutual exclusion. For anything beyond a simple flag, like incrementing a counter or updating multiple related fields together, you still need synchronization, locks, or atomic classes."