advanced.java
← All concepts
Concurrency · ch. 9, p. 68

Happens-before

A partial ordering on memory operations. If action A happens-before action B, everything A wrote is visible to B. Without such an edge, the JVM is free to reorder and a thread may read stale values indefinitely.

Happens-before is not about time. Two actions can be separated by minutes of wall clock and still have no happens-before edge between them, in which case the second one may observe a stale value forever.

Where the edges come from

  • Program order — within a single thread, earlier statements happen-before later ones.
  • Monitor lock — unlocking a monitor happens-before every subsequent lock of that same monitor.
  • Volatile — a write to a volatile field happens-before every subsequent read of it.
  • Thread startThread.start() happens-before any action in the started thread.
  • Thread join — every action in a thread happens-before another thread’s successful return from join() on it.
  • Transitivity — if A happens-before B and B happens-before C, then A happens-before C.

The classic failure

class Stopper {
    private boolean stop = false;          // not volatile

    void run() {
        while (!stop) { /* work */ }       // may hoist the read out of the loop
    }

    void requestStop() { stop = true; }    // no happens-before edge to run()
}

The JIT is entitled to hoist the field read out of the loop because nothing establishes an ordering. Marking stop as volatile creates the edge and the loop terminates.

What people get wrong

Assuming synchronized is only about mutual exclusion. It is equally about visibility — the lock release/acquire pair is what publishes the writes.

2 questions on this concept Drill it