The Java Memory Model — Visibility, volatile & happens-before
The bug: a flag you set is never seen
One thread flips running = false to stop a worker. The worker loops on while (running)
— and never stops, even though the flag was clearly written. Nothing crashes; the program just hangs. This is
a visibility bug, and it is the second pillar of concurrency after the race condition.
Why a write can be invisible
For speed, each CPU core keeps recently-used variables in its own registers and cache. A write by one thread may sit in that core's cache (or a register) and is not guaranteed to be flushed to main memory, nor is another thread guaranteed to re-read it. The compiler and CPU may also reorder instructions. Absent an explicit rule, one thread simply has no obligation to see another thread's writes.
Reproduce it
On a server JVM this commonly spins forever — the reader hoists running into a register:
class Worker implements Runnable {
private boolean running = true; // NOT volatile
public void run() { while (running) { /* hot loop */ } }
public void stop() { running = false; } // another thread calls this
}
// main: start worker, sleep 100ms, worker.stop(); -> may never return
The fix: establish a happens-before edge
The Java Memory Model guarantees visibility only across a happens-before relationship. The
cheapest one for a flag is volatile: a write to a volatile variable happens-before every
later read of it, so the read must come from main memory.
private volatile boolean running = true; // now stop() is always seen -> loop exits
The edges that create happens-before (the ones you actually use):
| Edge | Guarantees |
|---|---|
| volatile write → later read | the read sees that write (and everything before it) |
| unlock → later lock (same monitor) | everything before unlock is visible after lock |
Thread.start() | the new thread sees everything the starter did before start() |
Thread.join() | the joiner sees everything the joined thread did |
The trap: visibility ≠ atomicity
volatile makes a single read or write visible — it does not make
count++ atomic, because that is three operations (see Race Conditions). Rule of thumb:
volatile is for a flag or a single published reference. For a compound read-modify-write you need a lock or an atomic (next lesson).
Pitfalls
- Double-checked locking without a volatile field is broken — another thread can see a half-constructed object (reordering of allocation vs. field writes).
- 64-bit atomicity vs. visibility: JLS §17.7 guarantees that reads and writes of
longanddoubleare atomic — a single read or write will not see a torn half-value. The hazard for non-volatilelong/doubleis visibility: without a happens-before edge, a thread may keep reading a stale cached copy indefinitely. - "Works in the debugger": debugging/printing inserts memory barriers, hiding the bug. The hot release build hangs.
Takeaways
- Threads may cache variables; without a memory barrier, writes are not guaranteed visible.
- happens-before is the rule that grants visibility;
volatile, locks, start/join create it. volatile= visibility for one variable. It is not atomicity.
Re-authored for this guide; diagram hand-authored as SVG. Follows the Java Memory Model treatment in Java Concurrency in Practice (ch. 3) and Jenkov's concurrency tutorials. See also: Race Conditions, and Atomics & CAS.
🤖 Don't fully get this? Learn it with Claude
Stuck on The Java Memory Model — Visibility, volatile & happens-before? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **The Java Memory Model — Visibility, volatile & happens-before** (Concurrency) and want to truly understand it. Explain The Java Memory Model — Visibility, volatile & happens-before from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **The Java Memory Model — Visibility, volatile & happens-before** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **The Java Memory Model — Visibility, volatile & happens-before** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **The Java Memory Model — Visibility, volatile & happens-before** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.