In the previous article, we explored the transient keyword and learned how Java excludes variables from serialization.
In this article, we'll learn about another important keyword used in multithreaded programming:
volatile
When multiple threads share a variable, one thread may update its value while another thread continues to read an old (stale) value.
The volatile keyword helps ensure that changes made by one thread become visible to other threads.
By the end of this article, you'll understand:
- What
volatileis - Why it is needed
- How memory visibility works
- How
volatilediffers fromsynchronized - Illegal modifier combinations
- Advantages and disadvantages
- Common interview questions
- Best practices
What is the volatile Keyword?
The volatile keyword tells the JVM that a variable may be modified by multiple threads.
When a variable is declared as volatile, every read of that variable observes the most recently written value by any thread, according to the Java Memory Model.
The volatile modifier is applicable only to variables.
| Applicable To | Allowed? |
|---|---|
| Variable | ✅ |
| Method | ❌ |
| Class | ❌ |
| Constructor | ❌ |
Why Do We Need volatile?
Consider two threads sharing a boolean flag.
class Application {
boolean running = true;
}
Thread A changes the value:
running = false;
Thread B continuously checks:
while (running) {
// Do some work
}
Without proper memory visibility, Thread B may continue to see the old value and never exit the loop.
Declaring the variable as volatile solves this visibility problem.
volatile boolean running = true;
Understanding Memory Visibility
Each thread may keep frequently accessed variables in its working memory (such as CPU caches and thread-local views managed under the Java Memory Model).
Without volatile, one thread's updates may not become immediately visible to other threads.
Main Memory
│
┌─────────────┴─────────────┐
│ │
▼ ▼
Thread 1 Working Memory Thread 2 Working Memory
│ │
▼ ▼
Reads/Writes Reads Old Value
With volatile, reads and writes go through memory visibility guarantees defined by the Java Memory Model.
Thread 1
│
Writes volatile variable
│
▼
Main Memory (visibility guaranteed)
│
▼
Thread 2
Reads latest value
Basic Example
class Server {
private volatile boolean running = true;
public void stop() {
running = false;
}
public void start() {
while (running) {
// Server is running
}
System.out.println("Server stopped.");
}
}
Step-by-Step Explanation
Step 1
The server starts executing the loop.
Step 2
Another thread calls stop().
Step 3
The running variable is updated to false.
Step 4
Because running is volatile, the thread executing the loop observes the updated value and exits.
What Does volatile Guarantee?
A volatile variable provides:
- Visibility: Updates made by one thread become visible to other threads promptly.
- Ordering: The Java Memory Model prevents certain instruction reordering around volatile reads and writes.
However, volatile does not make compound operations atomic.
What volatile Does NOT Do
Consider the following example:
class Counter {
volatile int count = 0;
public void increment() {
count++;
}
}
Many beginners assume this is thread-safe.
It is not.
The statement:
count++;
actually performs three operations:
- Read
count - Add
1 - Write the new value
If two threads execute these steps simultaneously, updates can be lost.
For atomic increment operations, use synchronization or classes such as AtomicInteger.
volatile vs synchronized
| Feature | volatile |
synchronized |
|---|---|---|
| Applicable To | Variables | Methods and Blocks |
| Solves Visibility Problems | ✅ | ✅ |
| Provides Mutual Exclusion | ❌ | ✅ |
| Guarantees Atomicity for Compound Operations | ❌ | ✅ |
| Performance | Generally lower overhead | Higher overhead due to locking |
| Uses Locks | ❌ | ✅ |
When to Use volatile
Use volatile when:
- Multiple threads read a shared variable.
- One or more threads update that variable.
- The operation is a simple read or write (not a read-modify-write sequence).
Examples:
- Stop flags
- Shutdown signals
- Configuration refresh indicators
- Status flags
Illegal Combination: final volatile
final means:
"The value cannot change after initialization."
volatile means:
"The value may change, and updates must be visible to all threads."
These ideas contradict each other.
Incorrect:
final volatile int count = 10;
Compile-Time Error
illegal combination of modifiers: final and volatile
Advantages of volatile
1. Ensures Memory Visibility
Threads observe the latest value of the variable.
2. Simpler Than Synchronization
No explicit locking is required.
3. Better Performance for Simple Flags
For simple state variables, volatile often has lower overhead than synchronization.
Disadvantages of volatile
1. No Atomicity
Operations like count++ are still unsafe.
2. Not a Replacement for Synchronization
Complex operations involving multiple shared variables still require proper synchronization or concurrency utilities.
3. Easy to Misuse
Using volatile where atomicity is required can introduce subtle concurrency bugs.
Common Beginner Mistakes
Mistake 1: Assuming volatile Makes Everything Thread-Safe
Incorrect:
volatile int counter;
counter++;
counter++ is not atomic.
Mistake 2: Using volatile for Every Shared Variable
Only use volatile when you need visibility guarantees for simple reads and writes.
Mistake 3: Confusing Visibility with Atomicity
Visibility ensures that threads see the latest value.
Atomicity ensures that an entire operation completes without interference.
These are different concepts.
Mistake 4: Using volatile Instead of Proper Synchronization
If multiple variables must be updated together or operations involve invariants, volatile alone is insufficient.
Best Practices
- Use
volatilefor simple state flags shared across threads. - Avoid using
volatilefor counters that are incremented by multiple threads. - Prefer
AtomicInteger,AtomicLong, or otherjava.util.concurrent.atomicclasses for atomic updates. - Use synchronization or higher-level concurrency utilities for complex shared state.
- Understand the difference between visibility and atomicity before choosing
volatile.
Interview Questions
1. What is the purpose of the volatile keyword?
It ensures that updates to a variable made by one thread become visible to other threads.
Why interviewers ask
To test your understanding of the Java Memory Model.
2. Where can volatile be applied?
Only to variables.
3. Does volatile provide thread safety?
Not by itself.
It provides visibility and ordering guarantees, but not atomicity for compound operations.
4. What is the difference between volatile and synchronized?
volatile guarantees visibility.
synchronized guarantees both visibility and mutual exclusion.
5. Is count++ thread-safe if count is volatile?
No.
count++ is a compound operation and is not atomic.
6. Can methods be declared volatile?
No.
Only variables can use the volatile modifier.
7. Why is final volatile illegal?
Because final prevents changes after initialization, while volatile is intended for variables whose values may change and must be visible across threads.
Quick Memory Trick 🧠
Remember VIEW:
V → Visibility
I → Immediate visibility to other threads
E → Every read sees the latest write
W → Without locking (for simple reads and writes)
Think VIEW whenever you see volatile.
Key Takeaways
-
volatileis applicable only to variables. - It guarantees memory visibility between threads.
- It does not make compound operations like
count++atomic. -
volatileis ideal for stop flags, status indicators, and configuration flags. -
volatiledoes not replacesynchronized. -
final volatileis an illegal modifier combination. - Choose the right concurrency tool based on whether you need visibility, atomicity, or both.
If you found this guide helpful, leave a ❤️ and follow for more beginner-friendly Java tutorials, interview questions, and practical coding examples.
Happy Coding!
Top comments (0)