Home/Learn/Java A–Z/volatile Keyword

volatile Keyword

Advanced
Concurrency

volatile 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.

VolatileVisibility.java
// 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.

VolatileVsSync.java
// 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.

HappensBefore.java
// 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

NEWRUNNABLERUNNINGBLOCKEDWAITINGTERMINATED
synchronized(lock)— free
main
RUNNING
t1
NEW
t2
NEW
main thread creates Thread t1 and Thread t2. Both are in NEW state.
1 / 6

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 Aria
1

What problem does volatile solve that synchronized also solves?

MediumAmazon
2

Why is volatile insufficient for implementing a thread-safe counter?

MediumGoogle
3

Why is volatile required in the double-checked locking pattern?

HardOracle
4

What is the Java Memory Model and what guarantees does it provide?

HardMicrosoft
5

What is the happens-before relationship?

HardNetflix

Ask Aria about volatile Keyword

Your personal AI tutor — ask anything about this concept