The volatile keyword in Java

The volatile keyword is used in multithreaded Java programming. It is used as a field modifier (alongside private, public etc) to indicate that the field in question may be accessed by different Java threads. For example, we can declare that a boolean flag is accessed by multiple threads:

	private volatile boolean stopFlag = false;

Using volatile is one potential way of ensuring that your Java program is thread-safe and will behave as expected when multiple threads access the value in question. Java has various other language and platform features for dealing with multithreading, including the synchronized keyword. However, the volatile keyword is essentially lock-free and hence the most lightweight means of providing thread-safety.

What volatile actually does

The purpose of volatile is to ensure that when one thread writes a value to the field, the value written is "immediately available" to any thread that subsequently reads it. Conversely, other threads reading the value are "forced" to take that immediately available value, rather than relying on a value previously read and cached by those other threads. Without volatile or any other form of synchronization, then modern CPU architecture and optimisations can lead to unexpected results. A thread can work on a cached, out-of-date copy of the value in question, or not write it back to main memory in time for it to be accessed by another thread as expected.

Under the hood, volatile causes reads and writes to be accompanied by a special CPU instruction known as a memory barrier. The memory barrier forces the required execution ordering and memory visibility between the different CPU cores, so that to the programmer, reading and writing to the shared volatile field then behaves as expected.

You can think of volatile informally as being equivalent to automatically putting synchronized around each individual read or write of the field in question. And synchronized can be used to achieve a similar effect (also involving memory barriers under the hood). But synchronized is a more "heavyweight" solution under the hood because it also entails lock management, whereas volatile is lock-free.

When to use volatile

Given the above description, when is volatile actually used? In practice, volatile is most suitable:

For more information, see: when to use volatile.

When is volatile not suitable?

The volatile keyword is not usually suitable when:

Why is volatile not sufficient for incrementing a counter? The problem is that incrementing involves reading the current value, adding 1 to it, and then writing back the result. These three operations cannot necessarily happen in a single CPU instruction. Therefore, even with a volatile field, another thread could "sneak in" bewteen the read and the write operations. The result would be that we could miss an increment. With the simple example of a counter, we might as a programmer decide to take the risk. We might decide that the benefit of having a lock-free solution outweighs the consequence of missing an occasional increment. But where there would be actual harm from leaving the data in an inconsistent state, volatile would not be a suitable solution on its own.

volatile compared to synchronized etc

In view of our explanation of volatile so far, we can compare and contrast it to other thread-safety mechanisms in Java. The table below summarises these similarities and differences. Some of these differences affect what you can and cannot do functionally with volatile vs synchronized. Others are largely differences in what happens "under the hood" that we will discuss in more detail in the following sections. While not functional differences as such, these can still have knock-on consequences for the developer.

Type of variableObjectObject or primitive
Null allowed?NoYes
Can block/deadlock?YesNo
Suitable for maintaining consistency between multiple fieldsYesNo
Suitable for read-update-writeYesNot usually. See above example of a counter, plus the separate section on the Atomic get-set of volatiles
All cached variables synchronized on access?YesYes (since Java 5)
When synchronization happensWhen you explicitly enter/exit a synchronized blockWhenever a volatile variable is accessed.
Differences between synchronized and volatile

In other words, the main differences between synchronized and volatile are:

Attempting to synchronize on a null object will throw a NullPointerException.

Other details and examples of volatile

Strictly speaking, Java's definition of volatile is a little tighter than we have mentioned so far. We hinted at one or two of these details in the table above.

On the next page, we take a more detailed look at a typical example of using volatile. Later, we return to topics such as:

If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.