volatile Keyword
Advancedvolatile guarantees memory visibility across threads — changes to a volatile variable are immediately visible to all other threads.
Overview
The volatile keyword solves the visibility problem in Java concurrency. Without it, threads may read stale cached values from CPU registers or caches rather than the latest value in main memory. volatile establishes a happens-before relationship: a write to a volatile variable happens-before every subsequent read of that variable. It does NOT guarantee atomicity — volatile is not a replacement for synchronized when compound actions are needed.
Visibility Problem and volatile
Without volatile, the JVM and CPU may cache variable values in registers. A thread that writes to a variable may keep the new value in its local cache; other threads continue reading the old value.
volatile forces reads/writes to go directly to main memory, ensuring all threads see the latest value.
// WITHOUT volatile — loop may never terminate
public class VisibilityBug {
private static boolean stop = false; // no volatile
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
while (!stop) { /* may loop forever — sees cached false */ }
System.out.println("Stopped");
});
worker.start();
Thread.sleep(100);
stop = true; // worker may never see this update
}
}
// WITH volatile — guaranteed visibility
public class VisibilityFixed {
private static volatile boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
while (!stop) {}
System.out.println("Stopped");
});
worker.start();
Thread.sleep(100);
stop = true; // worker WILL see this update
}
}volatile vs synchronized
volatile: lightweight, no locking, solves visibility only. Use when one thread writes and others read, and the write is a single assignment (not compound).
synchronized: heavyweight, acquires lock, solves both visibility and atomicity. Required for compound operations (check-then-act, read-modify-write).
The classic rule: volatile for flags and status fields; synchronized (or Atomic classes) for counters and compound operations.
// volatile is CORRECT for a simple status flag
private volatile boolean running = true;
public void stop() { running = false; } // single write
public void run() { while (running) { process(); } }
// volatile is WRONG for a counter — still a race condition
private volatile int count = 0;
public void increment() {
count++; // NOT atomic: read → add → write (3 steps)
// volatile only ensures visibility, not atomicity
}
// CORRECT for a counter: use synchronized or AtomicInteger
private final AtomicInteger count = new AtomicInteger(0);
public void increment() { count.incrementAndGet(); } // atomic
// Double-checked locking requires volatile (Java 5+)
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // volatile prevents partial init
}
}
}
return instance;
}Happens-Before and Memory Model
The Java Memory Model (JMM) defines when one thread's writes are guaranteed to be visible to another. The happens-before relation establishes ordering guarantees.
Key happens-before rules: 1. Program order: each action in a thread happens-before subsequent actions. 2. Monitor lock: unlock of a monitor happens-before subsequent lock of the same monitor. 3. Volatile write: write to a volatile field happens-before subsequent reads of that field. 4. Thread start: Thread.start() happens-before any action in the started thread.
// Happens-before chain — volatile write → read
volatile int v = 0;
int x = 0;
// Thread A
x = 42;
v = 1; // volatile write — publishes x=42 too
// Thread B
if (v == 1) { // volatile read
// Guaranteed to see x = 42
// Because volatile write of v happens-before volatile read
System.out.println(x); // 42, not 0
}
// Safe publication via volatile
public class Config {
private volatile Map<String, String> settings;
public void reload() {
Map<String, String> newSettings = loadFromFile();
settings = newSettings; // volatile write — publishes entire map
}
public String get(String key) {
return settings.get(key); // volatile read — sees latest map
}
}Interactive Visualization
Key Points to Remember
- volatile guarantees visibility (all threads see the latest value) but NOT atomicity.
- Use volatile for simple status flags and single-assignment fields read by multiple threads.
- volatile does NOT make compound operations (count++) thread-safe — use AtomicInteger.
- volatile is required for double-checked locking to prevent partially-constructed object visibility.
- volatile establishes happens-before: a write happens-before all subsequent reads of that variable.
Practice volatile Keyword in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat problem does volatile solve that synchronized also solves?
Why is volatile insufficient for implementing a thread-safe counter?
Why is volatile required in the double-checked locking pattern?
What is the Java Memory Model and what guarantees does it provide?
What is the happens-before relationship?
Ask Aria about volatile Keyword
Your personal AI tutor — ask anything about this concept