DEV Community

Rajesh Bhola
Rajesh Bhola

Posted on

Part 7 - volatile Keyword: Memory Visibility, Thread Communication

In the previous article, we explored the transient keyword and learned how Java excludes variables from serialization.

In this article, we'll learn about another important keyword used in multithreaded programming:

volatile

When multiple threads share a variable, one thread may update its value while another thread continues to read an old (stale) value.

The volatile keyword helps ensure that changes made by one thread become visible to other threads.

By the end of this article, you'll understand:

  • What volatile is
  • Why it is needed
  • How memory visibility works
  • How volatile differs from synchronized
  • Illegal modifier combinations
  • Advantages and disadvantages
  • Common interview questions
  • Best practices

What is the volatile Keyword?

The volatile keyword tells the JVM that a variable may be modified by multiple threads.

When a variable is declared as volatile, every read of that variable observes the most recently written value by any thread, according to the Java Memory Model.

The volatile modifier is applicable only to variables.

Applicable To Allowed?
Variable
Method
Class
Constructor

Why Do We Need volatile?

Consider two threads sharing a boolean flag.

class Application {

    boolean running = true;

}
Enter fullscreen mode Exit fullscreen mode

Thread A changes the value:

running = false;
Enter fullscreen mode Exit fullscreen mode

Thread B continuously checks:

while (running) {

    // Do some work

}
Enter fullscreen mode Exit fullscreen mode

Without proper memory visibility, Thread B may continue to see the old value and never exit the loop.

Declaring the variable as volatile solves this visibility problem.

volatile boolean running = true;
Enter fullscreen mode Exit fullscreen mode

Understanding Memory Visibility

Each thread may keep frequently accessed variables in its working memory (such as CPU caches and thread-local views managed under the Java Memory Model).

Without volatile, one thread's updates may not become immediately visible to other threads.

               Main Memory
                    │
      ┌─────────────┴─────────────┐
      │                           │
      ▼                           ▼
Thread 1 Working Memory     Thread 2 Working Memory
      │                           │
      ▼                           ▼
Reads/Writes                Reads Old Value
Enter fullscreen mode Exit fullscreen mode

With volatile, reads and writes go through memory visibility guarantees defined by the Java Memory Model.

Thread 1
    │
Writes volatile variable
    │
    ▼
Main Memory (visibility guaranteed)
    │
    ▼
Thread 2
Reads latest value
Enter fullscreen mode Exit fullscreen mode

Basic Example

class Server {

    private volatile boolean running = true;

    public void stop() {
        running = false;
    }

    public void start() {

        while (running) {

            // Server is running

        }

        System.out.println("Server stopped.");

    }

}
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Explanation

Step 1

The server starts executing the loop.

Step 2

Another thread calls stop().

Step 3

The running variable is updated to false.

Step 4

Because running is volatile, the thread executing the loop observes the updated value and exits.


What Does volatile Guarantee?

A volatile variable provides:

  • Visibility: Updates made by one thread become visible to other threads promptly.
  • Ordering: The Java Memory Model prevents certain instruction reordering around volatile reads and writes.

However, volatile does not make compound operations atomic.


What volatile Does NOT Do

Consider the following example:

class Counter {

    volatile int count = 0;

    public void increment() {
        count++;
    }

}
Enter fullscreen mode Exit fullscreen mode

Many beginners assume this is thread-safe.

It is not.

The statement:

count++;
Enter fullscreen mode Exit fullscreen mode

actually performs three operations:

  1. Read count
  2. Add 1
  3. Write the new value

If two threads execute these steps simultaneously, updates can be lost.

For atomic increment operations, use synchronization or classes such as AtomicInteger.


volatile vs synchronized

Feature volatile synchronized
Applicable To Variables Methods and Blocks
Solves Visibility Problems
Provides Mutual Exclusion
Guarantees Atomicity for Compound Operations
Performance Generally lower overhead Higher overhead due to locking
Uses Locks

When to Use volatile

Use volatile when:

  • Multiple threads read a shared variable.
  • One or more threads update that variable.
  • The operation is a simple read or write (not a read-modify-write sequence).

Examples:

  • Stop flags
  • Shutdown signals
  • Configuration refresh indicators
  • Status flags

Illegal Combination: final volatile

final means:

"The value cannot change after initialization."

volatile means:

"The value may change, and updates must be visible to all threads."

These ideas contradict each other.

Incorrect:

final volatile int count = 10;
Enter fullscreen mode Exit fullscreen mode

Compile-Time Error

illegal combination of modifiers: final and volatile
Enter fullscreen mode Exit fullscreen mode

Advantages of volatile

1. Ensures Memory Visibility

Threads observe the latest value of the variable.


2. Simpler Than Synchronization

No explicit locking is required.


3. Better Performance for Simple Flags

For simple state variables, volatile often has lower overhead than synchronization.


Disadvantages of volatile

1. No Atomicity

Operations like count++ are still unsafe.


2. Not a Replacement for Synchronization

Complex operations involving multiple shared variables still require proper synchronization or concurrency utilities.


3. Easy to Misuse

Using volatile where atomicity is required can introduce subtle concurrency bugs.


Common Beginner Mistakes

Mistake 1: Assuming volatile Makes Everything Thread-Safe

Incorrect:

volatile int counter;

counter++;
Enter fullscreen mode Exit fullscreen mode

counter++ is not atomic.


Mistake 2: Using volatile for Every Shared Variable

Only use volatile when you need visibility guarantees for simple reads and writes.


Mistake 3: Confusing Visibility with Atomicity

Visibility ensures that threads see the latest value.

Atomicity ensures that an entire operation completes without interference.

These are different concepts.


Mistake 4: Using volatile Instead of Proper Synchronization

If multiple variables must be updated together or operations involve invariants, volatile alone is insufficient.


Best Practices

  • Use volatile for simple state flags shared across threads.
  • Avoid using volatile for counters that are incremented by multiple threads.
  • Prefer AtomicInteger, AtomicLong, or other java.util.concurrent.atomic classes for atomic updates.
  • Use synchronization or higher-level concurrency utilities for complex shared state.
  • Understand the difference between visibility and atomicity before choosing volatile.

Interview Questions

1. What is the purpose of the volatile keyword?

It ensures that updates to a variable made by one thread become visible to other threads.

Why interviewers ask

To test your understanding of the Java Memory Model.


2. Where can volatile be applied?

Only to variables.


3. Does volatile provide thread safety?

Not by itself.

It provides visibility and ordering guarantees, but not atomicity for compound operations.


4. What is the difference between volatile and synchronized?

volatile guarantees visibility.

synchronized guarantees both visibility and mutual exclusion.


5. Is count++ thread-safe if count is volatile?

No.

count++ is a compound operation and is not atomic.


6. Can methods be declared volatile?

No.

Only variables can use the volatile modifier.


7. Why is final volatile illegal?

Because final prevents changes after initialization, while volatile is intended for variables whose values may change and must be visible across threads.


Quick Memory Trick 🧠

Remember VIEW:

V → Visibility

I → Immediate visibility to other threads

E → Every read sees the latest write

W → Without locking (for simple reads and writes)
Enter fullscreen mode Exit fullscreen mode

Think VIEW whenever you see volatile.


Key Takeaways

  • volatile is applicable only to variables.
  • It guarantees memory visibility between threads.
  • It does not make compound operations like count++ atomic.
  • volatile is ideal for stop flags, status indicators, and configuration flags.
  • volatile does not replace synchronized.
  • final volatile is an illegal modifier combination.
  • Choose the right concurrency tool based on whether you need visibility, atomicity, or both.

If you found this guide helpful, leave a ❤️ and follow for more beginner-friendly Java tutorials, interview questions, and practical coding examples.

Happy Coding!

Top comments (0)