Java Concurrency + Memory Visibility
What is the difference between volatile and synchronized?
volatile gives visibility guarantees for a variable. synchronized gives visibility plus mutual exclusion, meaning only one thread can enter the critical section at a time.
The Short Answer
volatile makes changes to a variable visible across threads. In other words, if you have 100 threads, and thread 57 updates a volatile variable, then the other threads will see the latest value instead of working with an old cached copy.
But volatile does not stop multiple threads from updating the value at the same time. So if several threads are modifying the variable together, you can still get race conditions and lost updates.
synchronized also gives visibility guarantees, but it adds mutual exclusion too: only one thread can enter the synchronized block at a time.
Meaning that while one thread is updating the protected value, another thread cannot enter and modify it simultaneously. That is why synchronized can protect critical sections and keep multi-step operations consistent.
The Real Problem
In single-threaded code, when you update a variable, the next line of code sees the update. Simple.
In multi-threaded code, one thread changing a value does not automatically mean every other thread immediately sees the newest value.
Without volatile
Thread B may keep working with an old cached value because there is no visibility guarantee.
With volatile
volatile tells Java that reads and writes to this variable must be visible across threads.
Good Use Case for volatile
volatile is useful for a simple flag where one thread signals another thread to stop.
class Worker {
private volatile boolean running = true;
public void stop() {
running = false;
}
public void run() {
while (running) {
// do work
}
}
}Here, volatile is enough because reading and writing a boolean flag is simple. We only need visibility.
Where volatile Is Not Enough
volatile does not make compound operations atomic.
private volatile int count = 0;
public void increment() {
count++;
}This looks like one operation, but it is actually a read, modify, and write.
- Two threads can still read the same old value and overwrite each other's update.
- Imagine
countis currently 10. - Thread 1 reads 10 and decides to increment it to 11.
- But before Thread 1 writes 11 back to memory, 200 other threads may also read the old value 10 and independently decide that the next value should be 11.
- The result is that many threads may all write back 11, even though the correct final value should have been much larger.
When synchronized Is Better
Use synchronized when a block of code must be protected so only one thread can execute it at a time.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}Now increment is protected. One thread enters, updates the value, and exits. Another thread cannot enter that method at the same time.
Mental Model
volatile
Visibility Signal
synchronized
Locked Room
Interview-Friendly Answer
Common Interview Follow-Ups
Can volatile replace synchronized?
Only in simple visibility cases. It cannot replace synchronized when you need atomic multi-step updates.
Is count++ safe if count is volatile?
No. count++ is still read-modify-write, so multiple threads can lose updates.
Does synchronized also provide visibility?
Yes. Entering and exiting a synchronized block creates memory visibility guarantees.
When would you use AtomicInteger instead?
Use AtomicInteger when you need atomic operations like incrementAndGet without writing synchronized blocks yourself.