2.10 JMM Concurrency, Volatile & Memory Barriers
← Back to Chapter 2: Java Overview & Memory Model
The Java Memory Model (JMM), codified in JSR-133, defines the rules and guarantees governing how hardware CPU caches, compilers, and threads interact when reading and writing shared memory.
1. The Core Problem: Hardware Caches & Reordering
Modern CPUs have multi-level hardware caches (L1, L2, L3) to avoid slow main memory access:
Thread 1 (Core 1) Thread 2 (Core 2)
┌──────────────┐ ┌──────────────┐
│ L1 Cache │ │ L1 Cache │
└───────┬────── ┘ └───────┬──────┘
▼ ▼
┌───────────────────────────────────────────────────┐
│ Shared L3 Cache & RAM │
│ flag = true │
└───────────────────────────────────────────────────┘
Without synchronization, writes by Core 1 may remain buffered in its private L1 cache or write-buffer indefinitely, making changes invisible to Core 2. Additionally, the compiler and CPU may reorder instructions for performance.
2. The volatile Keyword
Declaring a field volatile provides two essential guarantees:
- Memory Visibility:
- Every write to a
volatilevariable flushes immediately from the CPU write-buffer to main memory. - Every read of a
volatilevariable bypasses CPU L1/L2 caches and loads directly from main memory.
- Every write to a
- Instruction Ordering via Memory Barriers:
- The JVM inserts hardware Memory Barriers (LoadLoad, LoadStore, StoreStore, StoreLoad) preventing the compiler or CPU from reordering instructions across the
volatileaccess boundary.
- The JVM inserts hardware Memory Barriers (LoadLoad, LoadStore, StoreStore, StoreLoad) preventing the compiler or CPU from reordering instructions across the
public class Worker implements Runnable {
// volatile guarantees that setting running = false in main thread
// is immediately observed by this worker thread!
private volatile boolean running = true;
public void stopWorker() {
this.running = false;
}
@Override
public void run() {
while (running) {
doTask();
}
}
}
volatile does NOT make compound operations atomic! For example, count++ involves three separate CPU instructions: read, increment, and write. For atomic numeric operations, use java.util.concurrent.atomic.AtomicInteger or explicit locks.
3. The "Happens-Before" Relationship
JMM defines formal ordering guarantees known as Happens-Before:
- Program Order Rule: Within a single thread, each action happens-before any subsequent action in program order.
- Monitor Lock Rule: An unlock on a monitor lock happens-before every subsequent lock on the same monitor.
- Volatile Variable Rule: A write to a
volatilefield happens-before every subsequent read of that same field. - Thread Start Rule: A call to
Thread.start()happens-before any action in the started thread.