Why a thread never sees your flag

Read the explanation
What if

main sets stop = true, but worker keeps spinning forever. Without volatile, Java doesn't promise worker ever sees the write.

Predict first

main sets stop = true after one second. What does worker do?

0123456789start()sleep(1_000)main: stop = truejoin(): foreverworker: loop, reads stoploopcompiled loop: stop read once

Tap any block to see what that thread is doing.

  • Running
  • Waiting / parked
Step 0/4

worker spins until stop becomes true.

1static boolean stop = false; // a plain field2 3public static void main(String[] args) throws Exception {4    Thread worker = new Thread(() -> {5        long spins = 0;6        while (!stop) { // the JIT may read stop once and never again7            spins++;8        }9        System.out.println("worker stopped after " + spins + " spins");10    }, "worker");11    worker.start();12    Thread.sleep(1_000);13    stop = true; // main writes the flag14    System.out.println("main set stop = true");15    worker.join(); // can wait forever16}

Explanation

What just happened

main set stop = true, but worker kept spinning. The loop was hot, so the JIT compiled it, and because stop is a plain field, the compiled code was allowed to read it once and reuse the value. worker never looked again, and join() never returned.

Try the what-if switch

volatile: a volatile field must be read from memory every time, and every write to it is visible to later reads on any thread. worker saw true on its next lap and exited.

Why Java allows this

Without volatile or a lock, the Java Memory Model doesn’t promise that one thread ever sees another’s write. That freedom lets the JIT and CPU keep values in registers and caches, which is what makes ordinary code fast. You opt in to cross-thread visibility where you need it.

Things that make a write visible:

Watch out

volatile fixes visibility, not atomicity. count++ on a volatile field is still read-add-write, so it still loses updates. Use it for flags written by one thread and read by others.

Common questions

Why does my thread never see a flag set by another thread?

Without volatile or a lock, the Java Memory Model doesn't promise one thread ever sees another's write. In practice the JIT compiles the hot loop to read the field once and reuse the value.

What does volatile do in Java?

Every read of a volatile field sees the latest write from any thread. That's why a loop checking a volatile stop flag exits on its next lap.

Is volatile enough for count++?

No. volatile fixes visibility, not atomicity. Use AtomicInteger or a lock for read-modify-write.

Verified: this timeline matches a real program, checked in CI on JDK 21 and 25. How we verify. Unclear or wrong? Tell us