What is the Java Memory Model, and what does 'happens-before' mean?

10 minadvancedjmmhappens-beforememory-model

Quick Answer

The Java Memory Model (JMM) defines the rules for when writes made by one thread are guaranteed to be visible to reads in another thread, since without synchronization the compiler, JIT, and CPU are all free to reorder or cache operations. 'Happens-before' is the JMM's core ordering relationship: if action A happens-before action B, then A's effects are guaranteed visible to and ordered before B. It's established by things like a monitor unlock happening-before a later lock on the same monitor, a volatile write happening-before a later volatile read of the same field, and a thread's actions happening-before a join() that observes its completion.

Detailed Answer

Without any synchronization, the Java Memory Model (JMM) gives almost no guarantee that one thread will see another thread's writes in any particular order, or at all — the compiler, JIT, and CPU are all allowed to reorder instructions and cache values in registers/CPU caches for performance, as long as a single-threaded program's observable behavior is unaffected. That freedom is exactly what causes subtle multithreaded bugs when code assumes an ordering the JMM never promised.

The happens-before relationship is the JMM's formal way of defining which cross-thread orderings are guaranteed. If action A happens-before action B, then: (1) A's effects (writes to memory) are visible to B, and (2) A is guaranteed to be ordered before B from the perspective of any thread observing both. Critically, happens-before does not mean "happened earlier in wall-clock time" — it's a guarantee the JMM enforces, not an incidental timing observation.

Key rules that establish happens-before (a partial list):

  • Program order rule: within a single thread, each action happens-before every subsequent action in that thread's program order.
  • Monitor lock rule: an unlock on a monitor happens-before every subsequent lock on that same monitor (by any thread) — this is why synchronized gives both mutual exclusion and visibility.
  • Volatile variable rule: a write to a volatile field happens-before every subsequent read of that same field.
  • Thread start rule: Thread.start() happens-before any action in the started thread.
  • Thread join rule: every action in a thread happens-before another thread successfully returns from join() on it.
volatile boolean ready = false;
int data = 0;

// Thread 1
data = 42;
ready = true;   // volatile write — happens-before any later read of `ready`

// Thread 2
if (ready) {    // volatile read
    print(data); // guaranteed to see 42, NOT 0, because of the happens-before edge
}

Without the volatile (or an equivalent lock), Thread 2 could legally observe ready == true but data == 0 — the JMM permits that reordering in the absence of an explicit happens-before edge. This is the theoretical foundation behind why synchronized, volatile, and the java.util.concurrent utilities all work: they each establish specific happens-before edges the JMM otherwise doesn't guarantee.