Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Concurrency & Multithreading

Threads & Runnable

  • Introduction to Threads
  • ExecutorService & Thread Pools

Synchronization & Memory Model

  • synchronized and Locks
  • volatile and the Memory Model
  • CompletableFuture

Concurrent Collections

  • ConcurrentHashMap & CopyOnWriteArrayList
Chaturmind
← Java Concurrency & Multithreading

Threads & Runnable

  • Introduction to Threads
  • ExecutorService & Thread Pools

Synchronization & Memory Model

  • synchronized and Locks
  • volatile and the Memory Model
  • CompletableFuture

Concurrent Collections

  • ConcurrentHashMap & CopyOnWriteArrayList
HomeLearnJavaJava ConcurrencyJava Memory Model
✓ FreeAdvanced· 13 min read

volatile and the Java Memory Model

Understand the Java Memory Model, happens-before relationships, and when volatile is the right tool.

Published February 13, 2025


volatile and the Java Memory Model

The Java Memory Model (JMM) defines how threads interact through shared memory. Without it, modern CPUs and compilers are free to reorder instructions and cache values in registers — leading to surprising concurrency bugs.

The Problem: Visibility

// Thread 1                    // Thread 2
boolean flag = false;           while (!flag) { } // may loop forever!
// ...
flag = true;

Without volatile, the JVM may cache flag in Thread 2's register. Thread 1's write to flag is invisible to Thread 2.

volatile — guaranteed visibility

private volatile boolean running = true;

// Thread 1: always reads the latest value from main memory
public void run() {
    while (running) {
        doWork();
    }
}

// Thread 2: write is immediately visible to all threads
public void stop() {
    running = false;
}

volatile guarantees:

  1. Visibility: writes to a volatile variable are immediately visible to all threads
  2. Ordering: prevents reordering of instructions around volatile reads/writes

volatile does NOT guarantee atomicity: counter++ on a volatile int is still a race condition.

Happens-Before Relationship

The JMM defines happens-before rules that guarantee memory visibility:

  1. Thread start: t.start() happens-before any action in thread t
  2. Thread join: all actions in thread t happen-before t.join() returns
  3. Volatile write: a write to a volatile field happens-before every subsequent read of that field
  4. Monitor unlock: unlocking a monitor happens-before every subsequent lock of that monitor
  5. Object construction: all actions in a constructor happen-before the finalizer
private volatile int value = 0;
private String data = null;

// Thread 1
data = "hello";     // write to data
value = 1;          // volatile write — creates happens-before

// Thread 2
if (value == 1) {   // volatile read — sees value = 1
    // data is GUARANTEED to be "hello" here
    // because volatile write happens-before volatile read
    System.out.println(data); // safe!
}

Instruction Reordering

CPUs and compilers reorder instructions for performance. The JMM allows this as long as the observable result within a single thread is the same. volatile inserts memory fences that prevent reordering.

Double-Checked Locking (Classic Pattern)

// BROKEN without volatile (reordering can expose partially constructed object)
public class Singleton {
    private static Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // can be reordered!
                }
            }
        }
        return instance;
    }
}

// CORRECT — volatile prevents the partial construction bug
public class Singleton {
    private static volatile Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

When to Use volatile

✅ Use volatile when:

  • A variable is written by one thread and read by others
  • You need a stop flag for a thread
  • You need the double-checked locking pattern

❌ Do NOT use volatile when:

  • Multiple threads are writing (use AtomicInteger, synchronized)
  • You have compound operations like check-then-act (use locks)

Interview Tips

  1. The volatile keyword is about visibility, not synchronization. Knowing this distinction separates candidates.
  2. synchronized provides both visibility AND atomicity; volatile only provides visibility.
  3. AtomicInteger provides both visibility AND atomic compound operations via CAS (Compare-And-Swap).

Previous

synchronized and Locks

Next

CompletableFuture

AI Tutor

Lesson: volatile and the Java Memory Model

Quick actions

AI responses can be inaccurate. Verify critical information.