The volatile keyword in Java is a modifier that marks a variable being stored in the main memory. Modifiers are specific keywords present in Java that are used to make changes to the characteristics of a variable, method or class and limit its scope.
Volatile Keyword in Java Definition
The volatile keyword in Java is a non-access modifier that signals to a thread that a variable is stored in the main memory, not the CPU cache. This ensures that all threads see the same value for the volatile keyword.
Modifiers in Java are divided into two types — access modifiers and non-access modifiers. Volatile is a non-access modifier.
What Is the Volatile Keyword in Java?
The volatile keyword in Java is used to mark a Java variable as “being stored in the main memory.” Every thread that accesses a volatile variable will read it from the main memory and not from the CPU cache. This way, all threads see the same value for the volatile variable.
The volatile keyword can be used with variables of any kind, including primitive types and object references. When used with objects, all threads will see the most up-to-date values for all fields in the object.
Volatile Keyword vs. Other Types of Keywords in Java
While the volatile keyword is often associated with other types of keywords in Java, there are clear differences between them. Here are other common types of keywords used in Java and what sets the volatile keyword apart.
Volatile vs. Atomic
Marking a variable as volatile means declaring that multiple threads will be accessing this variable. This makes the value of the variable visible to all threads. On the other hand, atomicity refers to an operation being indivisible. The atomic keyword hides a process from other threads until it’s been completed, making it appear as if it’s been accomplished in one step and preventing other threads from interfering in the process.
Volatile vs. Synchronized
Synchronization is an approach that allows only one thread at a time to access a specific section of code. This means that multiple threads cannot alter the block of code, or method, simultaneously. In this way, the synchronized keyword still supports visibility, but it also upholds atomicity by preventing other threads from interfering with a thread executing a task. This stands in contrast to the volatile keyword, which is only concerned with visibility.
Volatile vs. Transient
Both the volatile and transient keywords involve values, but they focus on different processes. The volatile keyword alerts users that a value can be changed by multiple threads simultaneously. Meanwhile, the transient keyword lets a program know to skip over a variable during serialization — the process of transforming an object’s state into a byte stream. The program then knows not to save the value of the variable.
Volatile vs. Static
The static keyword ensures that there is only ever one copy of a variable, regardless of how many objects of the class are generated. On the other hand, the volatile keyword enables a variable to be accessed by multiple threads at the same time, and each object created gets its own copy of the variable. This means that the static keyword operates on the class level while the volatile keyword operates on the level of instances or objects.
When to Use the Volatile Keyword
The volatile keyword is usually associated with multithreading, which refers to the ability of a computer program to simultaneously support multiple users without them having to create multiple copies of the program. As a result, multiple users can submit requests to the program at the same time.
Some scenarios involving multiple threads are ideal for the volatile keyword. It can be used to provide notification when two or more threads want to access specific instructions, when a value is updated by one thread and viewed by other threads and when flags, status indicators and other variables need to be managed.
While the volatile keyword in programming languages like Java can be applied to primitive data types (boolean, char, byte, int and so on) and object references, it is not considered suitable for complex data structures or mutable objects, as using “volatile” on such objects does not guarantee thread safety for operations that modify their internal state.
In particular, the volatile keyword is often used with flags that indicate a thread needs to stop running. For example, a thread might have a boolean flag called “done”
, and when another thread sets this flag to “true”
, the first thread will know to stop running. Without the volatile keyword, the first thread might keep running indefinitely, because it would never see the updated value of the “done”
flag.
There are other uses for the volatile keyword, but these are some of the most common. In general, if you need to make sure that all threads see the same value for a certain variable, mark that variable as being volatile.
How to Use the Volatile Keyword
Before diving into the example below, it’s important to understand what a singleton is. In Java, a singleton is a class that only has one instance, or object, at any given point. The volatile keyword can be applied here, making visible to all threads any changes made when a singleton instance is created. This helps prevent instructions from accidentally being reordered since users can see the work of other threads.
public class Singleton {
private static volatile Singleton _instance; // volatile variable
public static Singleton getInstance() {
if (_instance == null) {
synchronized (Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
Breaking Down the Example
In the code above, we’re using the Singleton design pattern in Java, ensuring that only one instance of the Singleton
class exists throughout the program.
The class has a private static variable _instance
that holds the single instance of the class. The keyword volatile
ensures that changes to _instance
are immediately visible across multiple threads, preventing caching issues.
In the case of getInstance()
, this method is used to get the single instance of the class. The first if (_instance == null)
check ensures that we only create an instance if one doesn’t already exist.
If _instance
is null
, we synchronize on Singleton.class
to make sure only one thread at a time can enter. Inside the synchronized block, we check again if _instance
is null
. This is called double-checked locking. If _instance
is still null
, we create a new instance.
Without double-checked locking, every thread would synchronize every time getInstance()
is called, which is inefficient. By checking _instance == null
before and after entering the synchronized block, we only lock when necessary. This improves performance while ensuring thread safety.
Explaining the Role of the Volatile Keyword
If we don’t make the _instance
variable volatile
, then the thread, which is creating the instance of Singleton
, isn’t able to communicate to the other thread. So, if Thread A
is creating a Singleton
instance, and just after creation, the CPU corrupts, all other threads will not be able to see the value of _instance
as not null
, and they will believe it is still assigned null.
Why does this happen? Because reader threads aren’t doing any locking, and until the writer thread comes out of a synchronized block, the memory will not be synchronized and the value of _instance
will not be updated in main memory. This is handled by Java itself with the volatile keyword, and such updates will be visible by all reader threads.
Limitations of the Volatile Keyword
Although the volatile keyword can make life easier for programmers, it still has some downsides that teams must consider to avoid major mistakes.
Lack of Atomicity
The volatile keyword’s inability to guarantee atomicity can become a larger issue during compound actions. While the volatile keyword can make sure individual actions are visible to all threads, it doesn’t guarantee atomicity for the entire compound operation. This can lead two threads executing the same compound action on the same variable at the same time to produce varying results.
Potential for Race Conditions
The absence of atomicity also means other threads can interfere in a process if a variable is volatile, creating a race condition. A race condition is a situation where two threads simultaneously access the same data, leading to an unpredictable result that depends on the order in which the threads perform their actions.
Complications With Arrays
Arrays present another situation where the volatile keyword is ineffective. Marking an array as volatile only applies to their references and doesn’t carry over to their fields. As a result, an array’s value in one thread may still not be visible in other threads.
Lower Computing Performance
When dealing with the volatile keyword, a computer must read volatile variables from a computer’s main memory instead of its CPU cache. This is a more demanding process that can slow down the computer and decrease its overall performance.
Frequently Asked Questions
What is the volatile keyword in Java?
The volatile keyword in Java signals that a variable is being stored in memory. Every thread that accesses a volatile variable will read it from main memory ensuring all threads see the same value for the volatile variable.
What are non-access modifiers in Java?
Non-access modifiers in Java provide information about the characteristics of a class, method, or variable to the JVM. There are seven non-access modifiers, including:
- final
- static
- abstract
- synchronized
- volatile
- transient
- native