skip to content

volatile Semantics

volatile guarantees visibility and prevents reordering around the access, but it does not make compound updates like count++ atomic. Interviewers use exactly that gap to separate people who have read about volatile from people who have used it.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

You have a worker thread looping on a non-volatile boolean flag that another thread sets to false, but it never stops. Why, and how do you fix it?

level: juniorimportance: must knowfreq 70%

answer

  1. JIT hoists flag into a register
  2. no happens-before = stale read
  3. make the flag volatile
  4. single boolean write is already atomic
  5. blocked threads also need interrupt()

basics

~10 s

The worker may keep reading a cached copy of the flag and never see the update. Declare the flag volatile so every read sees the latest written value, and the loop will exit.

solid answer

~40 s

Without any synchronization the JIT compiler is allowed to assume the loop variable doesn't change inside the worker thread, so it can hoist the flag into a register and never re-read it from memory — the loop spins forever even after another thread sets it false. There is no happens-before relationship forcing the writing thread's update to become visible to the reader. The fix is to make the flag volatile: a volatile read always returns the most recent write, and the volatile read/write pair establishes the happens-before edge that makes the update visible. Alternatives that also work are guarding the flag with synchronized or using an AtomicBoolean. volatile is the lightest correct option here because we only need visibility, not compound atomicity — a single boolean assignment is already a single atomic write.

code

java · 13 lines
java
class Server {
    private volatile boolean stopped = false; // fix: volatile

    void serve() {
        while (!stopped) {   // re-reads the field each iteration
            handleRequest();
        }
    }

    void shutdown() {
        stopped = true;      // promptly visible to the worker
    }
}

go deeper

for a junior

Recognizes the symptom (infinite loop) and the one-word fix (make the flag volatile) and can state that the worker was reading a stale copy.

for a middle

Explains JIT hoisting and the missing happens-before edge, and why volatile (vs a lock) is the appropriate minimal fix.

for a senior

Adds the caveat that volatile only helps between iterations (blocked threads need interrupt) and contrasts volatile/AtomicBoolean/synchronized trade-offs.

for a principal

Discusses the JMM optimization latitude that legalizes hoisting, safepoint behavior, and designs a robust shutdown protocol combining a volatile flag with interruption.

## The scenario ```java class Server { private boolean stopped = false; // BUG: not volatile void serve() { while (!stopped) { // worker thread handleRequest(); } } void shutdown() { stopped = true; // control thread } } ``` You call `shutdown()` from another thread, but `serve()` keeps running. Nothing is broken in the *value* — `stopped` is genuinely set to `true`. The problem is **visibility**: the worker thread never *observes* the change. ## Why it happens The Java Memory Model gives the compiler/JIT and CPU wide latitude to optimize code *as if it were single-threaded*. Inside `serve()`, nothing the thread itself does modifies `stopped`, so the JIT is permitted to **hoist** the field into a CPU register once and reuse it, effectively rewriting the loop to: ```java if (!stopped) { while (true) handleRequest(); } ``` There is also no **happens-before** edge — the JMM's ordering relation — connecting the write in `shutdown()` to the read in `serve()`. With no such edge, the JMM does *not* require the reader to ever see the writer's update. So the loop can spin forever, or stop only by luck (e.g. after a safepoint or on a less aggressive JVM). ## The fix ```java private volatile boolean stopped = false; ``` A **volatile** read is guaranteed to return the latest value written to that field by any thread, and a volatile write *happens-before* a subsequent volatile read of the same field. That forbids the hoisting optimization (the field must be re-read each iteration) and creates the visibility edge. The loop now exits promptly after `shutdown()`. ## Why volatile is the right tool here (not a lock) We need exactly one thing: **visibility** of a single boolean. We do *not* need atomicity of a compound operation (a plain `stopped = true` is already a single atomic write), and we do not need mutual exclusion. So volatile is the lightest correct choice. Heavier alternatives that also work: - `synchronized` getter/setter around `stopped` — correct but adds locking overhead and contention. - `AtomicBoolean stopped` — correct; useful if you later need `compareAndSet`, otherwise overkill for a plain flag. ## Important caveats - volatile fixes *this* because the operations are single reads and single writes. If the flag logic became a compound update (read-then-decide-then-write across threads), volatile alone would no longer be enough. - Marking the field volatile is *not* a substitute for proper interruption when the thread is blocked (e.g. inside `sleep`/`wait`/IO) — a volatile flag is only checked between iterations, so a blocked thread also needs `Thread.interrupt()`.

  • Would synchronized or AtomicBoolean also fix this?
    Yes, both establish the necessary visibility/happens-before. synchronized adds lock overhead, and AtomicBoolean is useful if you also need compareAndSet. For a plain flag that only needs visibility, volatile is the lightest correct choice.
  • If the worker is blocked inside sleep() or a socket read, will the volatile flag stop it?
    No — a volatile flag is only checked between loop iterations. A thread blocked in a blocking call won't reach the check, so you also need Thread.interrupt() to unblock it and then re-check the flag.

It's like a worker checking a sticky note they photocopied once and keep glancing at the copy; the boss updates the original on the wall but the worker never looks back at the wall. volatile forces the worker to read the wall every time.

saying these in an interview costs you the question

  • Saying the value 'wasn't actually written' — it was; the issue is visibility, not the assignment.
  • Claiming you must use a lock/synchronized — volatile suffices for a single-flag visibility need.
  • Thinking volatile will break a thread out of a blocking sleep/IO call.

context

open as a page

What does the volatile keyword do in Java, and what problem does it solve?

level: middleimportance: must knowfreq 80%

basics

~20 s

Marking a field volatile guarantees that a thread reading it always sees the most recent value written by any other thread. It fixes the visibility problem where one thread's update can otherwise go unseen by another.

open as a page

When would you choose volatile over synchronized or an Atomic class, and when is volatile insufficient?

level: middleimportance: must knowfreq 72%

basics

~20 s

Use volatile when you only need a single field's latest value to be visible across threads and you don't combine reads and writes. Use synchronized or an Atomic class when you need atomic compound updates (like count++) or to coordinate several fields together.

open as a page

If you declare a volatile array (e.g. volatile int[] data), which accesses get volatile semantics — and how do you get per-element visibility?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Only the array reference is volatile — reassigning the whole array variable is visible. Writing an element (data[i] = x) is NOT volatile. For per-element visibility use AtomicIntegerArray or VarHandle.

open as a page

Explain the happens-before guarantee of a volatile write/read and how you can 'piggyback' the publication of other data on it.

level: seniorimportance: should knowfreq 55%

basics

~20 s

A volatile write happens-before any later read of that same field. So everything a thread wrote before writing the volatile flag becomes visible to another thread once it reads the flag — you can publish plain data by writing the volatile flag last.

open as a page