Card 06/ 09

GotchaDifficulty: Advanced1 min

volatile: Why a Loop Never Sees the Flag Another Thread Set

The worker keeps running after shutdown, and attaching a debugger makes it stop.

volatile on a field makes every write to it visible to every thread, and every read fetch the current value. A loop that checks a stop flag without it may never see the flag change, and that is the most common visibility bug in Java: a write one thread makes that another thread never sees.

The shutdown request is logged, the flag is set, and the worker keeps running. Attaching a debugger makes it stop, which is the most confusing symptom possible:

java
boolean stopped = false;

while (!stopped) { doWork(); }

Nothing in the loop writes stopped, so the virtual machine is entitled to read it once and keep the value in a register, a slot inside the processor that is never refreshed from memory. From the single thread's own point of view that behaves identically, and it is why the loop never sees the change.

java
volatile boolean stopped = false;

volatile says that every read of this field must go and look, and every write must be published where other threads will see it. The loop now notices the change on its next pass.

volatile fixes visibility and nothing else. A volatile int still loses increments, because count++ is still three steps; they are now three steps every thread can see.