Concurrency
The Java Memory Model
- 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.
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
}
}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.