HOWTO · Java

The volatile Keyword in Java

This article discusses the volatile keyword in Java, its advantages and disadvantages, and uses with example.

On this page

Use volatile when threads share a field whose latest value must be visible across threads, such as a stop flag. It does not make a sequence of operations atomic. This guide explains the visibility guarantee, when the keyword is sufficient, and how to use it to stop a worker thread.

Understand volatile Visibility

A thread may otherwise reuse a value it has already read or keep a value in a way that does not promptly reflect another thread’s write. Declaring a field volatile gives reads and writes to that field the Java Memory Model’s visibility and ordering guarantees. When one thread writes a volatile field and another thread subsequently reads that same field, the write happens-before that read. The reader therefore observes that write or a later write to the field, rather than relying on a stale cached value.

This is a defined synchronization relationship, not a promise that every thread sees a change at the same instant. A thread that never reads the field again cannot react to it, and a volatile access does not synchronize unrelated ordinary fields by itself. Keep the communication protocol clear: use the same volatile field for the signal, and use another synchronization mechanism if other shared state must be coordinated with it.

When volatile Is Appropriate

Use a volatile field when its value can be read and replaced independently, and the decision to write it does not depend on an earlier read that another thread may also change. A boolean shutdown flag is a common example: one thread sets it, while a worker repeatedly checks it. A configuration or status value that is replaced as a whole can also fit, provided its surrounding invariants do not require a larger atomic update.

Ask whether each write can stand on its own. If a thread must first inspect shared state and then update one or more fields consistently, use a lock or a suitable concurrent data structure. volatile alone does not make that multi-step protocol safe.

What volatile Does Not Guarantee

A volatile read or write is individually visible and ordered, but compound actions are not made indivisible. For example, count++ reads count, adds one, and writes the result. Two threads can read the same old value and overwrite one another’s updates even if count is volatile. Use AtomicInteger for a suitable atomic numeric update, or protect the complete operation with synchronized or another lock when it belongs to a larger invariant.

Likewise, declaring several fields volatile does not make a group of updates happen as one transaction. Choose synchronization based on the whole operation the program needs, not just on whether a field is shared.

Use a volatile Flag to Stop a Thread

The worker below checks a shared flag, while the main thread clears it after a short wait. Thread.start() creates a separate worker thread; calling run() directly would execute the method on the caller instead. The example is written as a self-contained Java class. The supplied successful run exited with code 0; its expected stdout is shown after the program.

Run this example:

public class Main {
    private static volatile boolean running = true;

    public static void main(String[] args) throws Throwable {
        Thread worker = new Thread(() -> {
            while (running) {
                // Do a small unit of work here.
            }
            System.out.println("Worker stopped");
        });

        worker.start();
        Thread.sleep(50);
        running = false;
        worker.join();
    }
}
Worker stopped

The worker can finish only after it observes the main thread’s write of false. join() makes the main thread wait for the worker to finish before the program exits. In a real worker, keep each unit of work short enough to check the flag regularly; a long blocking call may need interruption or another cancellation mechanism as well. This loop is only a compact illustration and would consume CPU while it waits.

Compare volatile with synchronized

volatile is useful for a simple visibility signal or an independently replaced value. It does not acquire a monitor, prevent simultaneous execution of a critical section, or make compound updates atomic. synchronized both establishes visibility through monitor entry and exit and allows code guarded by the same monitor to run as a mutually exclusive operation. It is appropriate when several steps or fields must remain consistent.

Neither choice has a universal performance advantage. The cost depends on contention, access patterns, the JVM, and the work protected. Prefer the mechanism whose guarantees match the task, then measure the actual application if performance matters.

Key Takeaways

  • Use volatile for a simple shared signal or independently replaced field that needs cross-thread visibility.
  • A volatile write and a subsequent read of that field form a happens-before relationship.
  • Do not use volatile alone for increments, read-modify-write logic, or invariants spanning multiple fields.
  • Use an atomic class for supported single-value updates, or synchronized when a whole operation must be coordinated.