Loslegen
Javaneer
Zurück zur Stufe
Stufe 7·Advanced Concurrency & Modern Java

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.

16 Min. LesezeitExperte

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 write

Without 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. Marking running volatile 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 threads

Safe 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.

Two people editing with private notebooks

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.

Fix the stop-flag loop

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.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten