Concurrency

The Java Memory Model

The JMM defines which writes a read may observe, via the happens-before partial order. If two accesses to the same variable aren't ordered by happens-before and one is a write, that's a data race — and "sequential consistency" intuition no longer applies.
  • Compilers and CPUs reorder aggressively; the JMM is the contract that tames it
  • happens-before sources: program order, monitor unlock→lock, volatile write→read, Thread.start, Thread.join, final-field freeze
  • Data race = concurrent conflicting accesses without happens-before ordering
  • Synchronization "piggybacks": one volatile/lock edge orders all prior writes, not just the flagged variable
  • The double-checked-locking idiom is broken without volatile

Modern performance rests on reordering: compilers hoist, CPUs execute out of order, store buffers delay visibility (Hardware Memory). Single-threaded code can't tell — the JIT preserves as-if-serial semantics per thread. Between threads, only happens-before edges constrain what a read can return. No edge → the read may see a stale value, a later value, or (for 64-bit non-volatile longs/doubles, in theory) a torn value.

Piggybacking: one edge orders everything before it
class Handoff {
    private int payload;                 // plain field — no volatile needed
    private volatile boolean ready;

    void produce() {
        payload = compute();             // ①
        ready = true;                    // ② volatile write
    }
    void consume() {
        if (ready)                        // ③ volatile read that sees ②
            use(payload);                 // ④ guaranteed to see ① — happens-before is transitive
    }
}
Double-checked locking — correct only with volatile
private static volatile Helper instance;      // volatile is load-bearing!

static Helper getInstance() {
    Helper h = instance;                       // local read (one volatile access)
    if (h == null) {
        synchronized (Helper.class) {
            h = instance;
            if (h == null) instance = h = new Helper();
        }
    }
    return h;
}
// Without volatile, another thread can see a non-null reference
// to a Helper whose constructor writes haven't been made visible.
// Simpler alternatives: holder-class idiom, or an enum (EJ 83).

The lazy initialization holder class idiom gets the same laziness from the class loader's own synchronization, with zero volatile subtlety: static class Holder { static final Helper INSTANCE = new Helper(); } — the JVM guarantees class initialization is safely published (Class Loading). Effective Java Item 83: prefer it; and mostly, don't lazy-initialize at all.

Sources
  • Java Concurrency in PracticeCh. 16 — The Java Memory Model
  • Optimizing JavaCh. 12 — Concurrent Performance Techniques
  • Core Java, Volume I: Fundamentals (13th ed.)Ch. 10.5 — Synchronization