In this tutorial we will examine the Java volatile keyword. The Java volatile keyword offfers a weaker from of synchronization.
The purpose of the volatile keyword is to ensure that updates made to a variable declared with volatile are visible and propagted to other threads. When a variable is declared with volatile it tells the Java compiler that the variable should not be cached in a threads working memory such as caches and registers where the value is not visible to other threads.
The volatile keywords ensures that when a volatile variable is written to the updated is always propaged to main memory for other threads to read. It also ensures that the compiler does not reorder operations.
First off using the volatile keyword can offer a weaker form of synchronization than using the Java synchronized keyword. The volatile keyword is useful when we have a variable that can act independently from other mutable state declared in an objects variables. The volatile variable can be used when updates to the variable does not depend on its current value, and the variable does not participate in invariants with other variables
In this case the volatile variable is best used as a status flag, such as done, sleep, interruption flag etc. The volatile flag is useful as it tells the Java compiler not to cache and perform optimizations with the variable.
It is not recommend to use the volatile if the variable if the variable to be marked as volatile has a relationship with other state variables of the object and the correctness of the object depends on this composition, then a volatile variable is probably not the best option in this scenario. Moreover, using multiple volatile variables is not atomic, this is because the interaction with a volatile variable is handled separately then atomically across all volatile variables.
Volatile variables should also not be used for increment operations as this results in three separate operations:
In this case it is possible for threads to see different values of the variable and the interleaving of operations can result in unpredictable results. If we need to perform such operations an alternative approach is to use Java Atomic variables.
As we said volatile is useful when we require a status flag. For example consider the below code that uses a status flag named done that is used to stop a secondary thread when done is set to true.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.stephenenright.tutorials.corejava.concurrency.volatilevariables;
public class VolatileExample {
static boolean done = false;
public static void main(String[] args) {
System.out.println("Program Starting");
new Thread() {
public void run() {
System.out.println("Starting Secondary Thread");
while (!done) {
}
System.out.println("Secondary Thread Terminating");
}
}.start();
try {
Thread.sleep(700);
} catch (InterruptedException e) {
System.out.println("Thread Interrupted :" + e);
}
done = true;
System.out.println("Program Terminating");
}
}
|
If we run the above code the program will never terminate, the write on line 24 to set the variable done to true is never seeing by the secondary thread that we started. This is because the main thread stores the update in its local memory and not main memory.
1 2 3 | Program Starting
Starting Secondary Thread
Program Terminating
|
We can fix the problem be declaring the done variable as volatile.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class VolatileExample {
static volatile boolean done = false;
public static void main(String[] args) {
System.out.println("Program Starting");
new Thread() {
public void run() {
System.out.println("Starting Secondary Thread");
while (!done) {
}
System.out.println("Secondary Thread Terminating");
}
}.start();
try {
Thread.sleep(700);
} catch (InterruptedException e) {
System.out.println("Thread Interrupted :" + e);
}
done = true;
System.out.println("Program Terminating");
}
}
|
When we run the program again we see the program terminates as expected and we see the following output:
1 2 3 4 | Program Starting
Starting Secondary Thread
Program Terminating
Secondary Thread Terminating
|
When we mark the done variable as volatile the write to set variable done to true, is written to main memory. Therefore, the secondary thread that we start can see this modification, exits out of the while loop and terminates.
To recap what we discussed in the previous sections, the volatile keyword provides a weaker form of synchronization. What this means is that for a concurrent Java application to perform correctly when working with shared mutable state it needs to ensure the following properties:
The volatile keyword can provide some visibility guarantees when used in the correct scenarios, while synchronized methods and synchronized blocks provide both mutual exclusion and visibility.
In this tutorial, we looked at the volatile keyword in Java. Moreover, we looked at some scenarios where the volatile keyword should and should not be used. Finally we looked at the difference between the volatile keyword and synchronized blocks.