Q31 of 40 · Core Java

Explain the Java Memory Model and the happens-before relationship.

Core JavaSeniorjmmvolatileconcurrencyhappens-beforevisibilitythreading

Short answer

Short answer: The Java Memory Model (JMM) defines the rules for when one thread's writes are guaranteed to be visible to another thread. It does this through the happens-before (HB) partial order: if action A happens-before action B, then A's effects are visible to B. Without an HB edge, the JVM and CPU are free to reorder or cache writes invisibly.

Detail

Why HB is needed Modern CPUs use store buffers, caches, and out-of-order execution. Without explicit HB edges, Thread 2 may never see Thread 1's write, or may see writes in a different order.

HB rules (key ones)

  1. Program order — each action in a thread HB the next action in that thread.
  2. Monitor lock — unlock of a monitor HB every subsequent lock of that same monitor.
  3. volatile write — a write to a volatile field HB every subsequent read of that field.
  4. Thread startThread.start() HB any action in the started thread.
  5. Thread join — all actions in thread T HB T.join() returning.
  6. Transitivity — if A HB B and B HB C then A HB C.
// Pattern: use a volatile flag to safely publish an initialised object
class DataPublisher {
    private int value = 0;          // not volatile
    private volatile boolean ready = false; // volatile provides HB

    // Thread 1
    public void publish() {
        value = 42;          // write 1
        ready = true;        // volatile write — HB edge established
    }

    // Thread 2
    public void consume() {
        if (ready) {         // volatile read
            // Because volatile-write HB volatile-read, and
            // program order gives write1 HB volatile-write,
            // transitivity guarantees Thread 2 sees value == 42
            System.out.println(value);  // guaranteed: 42
        }
    }
}

When volatile is NOT enough HB only guarantees visibility — it does not make compound operations atomic.

import java.util.concurrent.atomic.AtomicInteger;

private volatile int count = 0;

// BROKEN — not atomic even with volatile
count++;  // read + increment + write — two threads can interleave

// Correct
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // single CAS operation

QA relevance: Selenium/Playwright shared driver instances, static test state, and @BeforeAll setup data all benefit from correct HB reasoning. A flaky test that only fails under parallel execution often has a missing HB edge.

// WHAT INTERVIEWERS LOOK FOR

Ability to name at least 3 HB rules. The difference between visibility (HB guarantees) and atomicity (which HB does NOT guarantee). Knowing that volatile write → volatile read is the canonical lightweight HB edge without locking. Senior candidates mention the reordering freedom the JVM retains when no HB exists.

// COMMON PITFALL

Saying 'volatile makes operations atomic.' Volatile prevents stale reads and write reordering around the volatile access — it does not serialise compound operations like check-then-act or read-modify-write.