Java Memory Model and Happens-Before

Naveen AnamaneniUpdated · 5 min read

The Java Memory Model (JMM) is the part of the Java specification that answers one question: when is a value written by one thread guaranteed to be seen by another thread? Compilers and CPUs reorder and cache memory operations to run faster, which is invisible within one thread but can surprise other threads. The JMM defines a relation called happens-before: if action A happens-before action B, then B is guaranteed to see the effects of A. In this lesson, you will learn why such rules are needed and the happens-before rules you use every day.

Why a Memory Model Is Needed

Within one thread, Java guarantees that code behaves as if it ran in program order. To be fast, though, the JIT compiler, the CPU and its caches may:

  • Reorder independent reads and writes,
  • Keep values in registers or caches instead of main memory,
  • Remove reads they think are repeated.

Other threads can observe these effects. The JMM says exactly which orderings and values another thread is allowed to see, and gives you tools to forbid surprises.

The Happens-Before Rules

If A happens-before B, then all writes made before A are visible to B, and they aren't reordered across that edge.

RuleHappens-before edge
Program orderEach action in a thread happens-before every later action in the same thread
Monitor lockReleasing a lock happens-before every later acquire of the same lock
VolatileA write to a volatile field happens-before every later read of that field
Thread startt.start() happens-before every action in thread t
Thread joinEvery action in t happens-before another thread returns from t.join()
Interruptiont.interrupt() happens-before t detects the interrupt
TransitivityIf A happens-before B and B happens-before C, then A happens-before C
Final fieldsA properly constructed object's final fields are visible to all threads
A volatile write and a later read link the two threads, so earlier writes become visible

Example: Publishing Data Safely

Let's see an example that uses a volatile flag to publish ordinary data from one thread to another.

Publish.java▶ Try it
public class Publish {
    static int[] marks;
    static String owner;
    static volatile boolean ready = false;
 
    public static void main(String[] args) throws InterruptedException {
        Thread reader = new Thread(() -> {
            while (!ready) {
                Thread.onSpinWait();
            }
            System.out.println(owner + ": " + marks[0] + ", " + marks[1]);
        });
        reader.start();
 
        marks = new int[] {91, 85};
        owner = "Navika";
        ready = true;
        reader.join();
    }
}
Output
Navika: 91, 85

Explanation: marks and owner are plain fields, but they are written before the volatile write ready = true. The reader reads ready (volatile) before reading them. By program order, the volatile rule and transitivity, everything written before ready = true is visible after the reader sees true. Without volatile, the reader could loop forever or even see ready as true but owner still null.

Everyday Ways to Get Happens-Before

  • Guard shared data with the same lock for every read and write.
  • Use volatile for flags and for publishing references.
  • Start threads after setting up their data; join before reading their results.
  • Use java.util.concurrent classes (queues, latches, atomics, ConcurrentHashMap): their methods create happens-before edges between the threads that use them.
  • Make objects immutable, with final fields.

Common Mistakes

Assuming a write is visible because it happened "earlier" in real time. Without a happens-before edge, it may not be.

Using different locks for writes and reads. Only the same lock creates the edge.

Letting this escape from a constructor. Other threads may see the object before its fields are set.

Interview Corner

What is happens-before in the Java Memory Model?

Happens-before is a partial ordering defined by the JMM: if action A happens-before action B, the results of A, and of everything that happened-before A, are visible to B and won't be reordered after it. Edges are created by program order within a thread, releasing a monitor before a later acquire of it, a volatile write before a later read of it, Thread.start before the started thread's actions, a thread's actions before another's return from join, and transitivity. Data races occur when conflicting accesses aren't ordered by happens-before.

Practice

  1. Easy: For the JoinResult example in "Joining Threads in Java", name the happens-before rule that makes the worker's result visible to main.
  2. Harder: Remove volatile from ready in Publish, run it several times, and explain why the result is no longer guaranteed even if it seems to work.

Quick check: Java Memory Model

4 questions
1What does the JMM define?
2Which creates a happens-before edge?
3A thread writes data, then a volatile flag. Another reads the flag as true, then reads data. Is data visible?
4What does join() guarantee?

Key Takeaways

Was this tutorial helpful?

Lesson 409 of 482