I have been asking developers these days if they know why do we need to use volatile in java. Most of the answers that I received were like reading a definition from a book and like nobody cared to dig a little bit to figure out what is going on in the background. In this article, I am going to talk about why to use it and what will happen to the cache memory when we are using volatile
.
Let’s say we are writing an application that will turn on the electricity and of course turn it off and in it we have two threads for each action. One of the threads is responsible to turn the electricity on and the second thread will turn the electricity off. We will also have a circuit breaker which when set to true will turn on the electricity, and when set to false will turn off the electricity. Let’s look at how that will look in java:
public class ElectricityDemo {
public static boolean circuitBreaker = true;
public static void main(String[] args) {
Thread electricityOn = new Thread(() -> {
while (circuitBreaker) {
System.out.println("POWER ON");
}
});
Thread electricityOff = new Thread(() -> circuitBreaker = false);
electricityOn.start();
electricityOff.start();
}
}
So, as we can see in the code, while variable circuitBreaker
is set to true we will print the POWER ON and as soon as this variable is set to false in the electricityOff
thread, we will expect for electricityOn
to stop running.
However, this might not be the case and let’s see the reason for it. On the picture below we have a CPU with two cores and the value to which the circuitBreaker
is set:
As we can see on the picture, the thread electricityOn
is running on core1 and electricityOff
on core2. Each core has its own local cache and there is a shared cache. Since both of the threads are using the variable circuitBreaker
they will load it in their local cache. From the code we can see that local cache 1 will have the variable value set to true
and the local cache 2 will have the new value false
. Unfortunately, this update of the circuitBreaker = false
in the local cache 2 won’t be visible to local cache 1, so when thread electricityOff
will set the variable circuitBreaker
to false this information won’t be visible in the local cache 1 and this is what we call a visibility problem. In order to solve this issue we want to make the value of the variable visible to local cache 1. This visibility problem can be solved by making the circuitBreaker
volatile
.
So, our code will look like this:
public class ElectricityDemo {
public static volatile boolean circuitBreaker = true;
public static void main(String[] args) {
Thread electricityOn = new Thread(() -> {
while (circuitBreaker) {
System.out.println("POWER ON");
}
});
Thread electricityOff = new Thread(() -> circuitBreaker = false);
electricityOn.start();
electricityOff.start();
}
}
Let’s look at the picture below to see what is happening now:
When setting the variable to volatile
, we are pushing this information to the shared cache and now the value will be false
. Additionally, the local cache 1 will be refreshed and it will have the actual value of the variable. Finally the electricityOn
will have the correct value of the circuitBreaker
and will stop the electricity.
Every time when we have two or more threads accessing a variable and executing actions based on the variable value it will be smart decision to set that variable as a volatile
.
So we solved our issue with the electricity running on the whole time. Hope you enjoyed it and saw the actual benefit of adding volatile to your variables.
Technically, static volatile fetches the value from “main memory” instead of local cache, isn’t it?