The Java Memory Model
Visibility, happens-before, and safe publication - why a value written by one thread may never be seen by another, and how to fix it.
Deutsche Übersetzung in Arbeit
Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.
Auf dieser Seite
You know threads and synchronized from the core path. This stage goes deeper, and it starts with the
thing that trips up even experienced engineers: the Java Memory Model (JMM). It answers a subtle
question - when is a value written by one thread guaranteed to be visible to another? - and the answer
is "less often than you'd think."
The visibility problem
Threads don't read and write directly to main memory on every access - values can sit in CPU caches or
registers. So a write by one thread may be invisible to another indefinitely. This loop can run
forever even after another thread sets running = false:
boolean running = true; // NOT volatile
// thread A
while (running) { /* work */ } // may never see running become false
// thread B
running = false; // A might never observe this writeWithout a memory-model guarantee, the JVM and CPU are free to cache running, reorder instructions, or
optimize the loop assuming running never changes. The bug is invisible in testing and catastrophic in
production.
happens-before: the ordering guarantee
The JMM defines a happens-before relationship: if action X happens-before action Y, then X's effects are guaranteed visible to Y. Several things establish it:
volatile- a write to a volatile field happens-before every subsequent read of it. Markingrunningvolatile fixes the loop above (visibility, though not atomicity of compound operations).synchronized- unlocking a monitor happens-before any later lock of the same monitor, so everything done inside a synchronized block is visible to the next thread that enters it.- Thread start/join, final fields, and the concurrent utilities (locks, atomics,
CountDownLatch) all establish happens-before edges.
volatile boolean running = true; // now writes are visible across threadsSafe publication
Even object construction has a subtlety: another thread might see a partially constructed object if
you publish a reference without proper synchronization. Safe publication - via a volatile field, a
synchronized block, a final field, or a concurrent collection - guarantees other threads see the fully
built object. This is why immutable objects with final fields are so thread-friendly: their safe
publication is automatic.
volatile gives visibility, not atomicity
A common mistake: making a counter volatile and expecting count++ to be thread-safe. It isn't - count++ is read-modify-write, three operations, and two threads can interleave and lose an update. volatile guarantees each read sees the latest write (visibility), but not that a compound operation is indivisible (atomicity). For that you need AtomicInteger or synchronization - the next lessons.
Imagine two colleagues who each keep a private notebook and only occasionally sync it to a shared
whiteboard. If one writes 'meeting cancelled' in their notebook but never copies it to the whiteboard,
the other keeps showing up. volatile is the rule 'always write this fact straight to the whiteboard and
always read it from there' - so the update is seen. happens-before is the broader agreement about which
notebook entries are guaranteed on the whiteboard by the time the other person looks. Without such rules,
each thread reasons from its own stale notebook, and updates silently vanish.
A worker thread spins in while (running) { ... } and a controller thread sets running = false to stop
it, but the worker sometimes never stops. Explain the memory-model cause and two correct fixes.
What does declaring a field volatile guarantee?
Key takeaways
- The Java Memory Model governs when one thread's writes become visible to another - values can be cached, so writes aren't automatically seen.
- happens-before is the ordering guarantee: if X happens-before Y, X's effects are visible to Y.
- volatile, synchronized, thread start/join, final fields, and concurrent utilities all establish happens-before edges.
- volatile gives visibility but NOT atomicity - i++ on a volatile field can still lose updates.
- Safe publication (volatile, final, synchronized, concurrent collections) ensures other threads see a fully constructed object; immutable objects get it automatically.