In this article we will discuss field visibility problems that occurs sometimes in Java programs. We will talk about their causes, how to prevent them, and we will see some techniques and considerations we need to keep in mind when working with multithread programs in Java.
Dealing with visibility issues
The volatile keyword was introduced in Java since version 5 of Java (Java 5). In Java, there are two areas where the program threads read the content of the variables. These areas are the main memory and the CPU cache. The CPU cache is used to improve performance. So what is volatile in Java? Volatile is a keyword used in Java to mark variables as “saved in main memory”. When a variable is marked as volatile every time the program reads the variable it will read from the computer’s main memory, and not from the CPU cache, and also it means that every time it writes to the volatile variable it will write to the main memory, not just the CPU cache.
The problems of visibility of variables occur when the content of the variables is in more than one location, therefore there is a possibility that the variables are not synchronized at some point in time. In other words, the value of a variable “x” in the main memory might not match the value of the same variable “x” in the CPU cache.
We need some way to ensure that these values are identical in both areas. Using the volatile keyword we guarantee that the content of the variables is consistent across threads.
For performance reasons and in multithreaded applications where threads work with non-volatile variables, each thread might create a copy of the variables in a CPU cache. If the computer has more than one CPU, then there may be a copy of the same variable in several different CPU caches.
For example, thread 1 is running in CPU 1 and is using the variables stored in CPU 1 cache, meanwhile thread 2 might be running in CPU 2 and is using the variables stored in CPU 2. If the variable is not declared with the volatile keyword there are no guarantees about when the Java Virtual Machine writes data from CPU caches into main memory, or, reads data from main memory into CPU caches. This can cause visibility problems.
Let us look at a scenario. Non-Volatile variable var1 equals 0 in main memory and also var1 equals 0 in CPU1 cache. Thread 1 uses a copy of var1 from the CPU1 cache. Thread 2 uses a copy of var1 from CPU2 cache. var1 equals 0 in CPU2 cache. So far so good, var1 is 0 is the main memory and is 0 in CPU1 cache and also is 0 in CPU2 cache. Now Thread 1 changes var1 to 9 into CPU1 cache. Since the var1 is not declared with the volatile keyword, then we have a discrepancy because, without the volatile keyword, the Java Virtual Machine is not guaranteed to change the main memory value immediately after the change of value in the CPU1 cache.
Main memory var 1 = 0.
CPU1 cache var 1 = 9.
CPU2 cache var 2 = 0.
Since CPU2 is not “seeing” the latest value of variable var1 (which is 9) we have what is called a “visibility” problem. The volatile keyword helps resolve this problem.
Solving the visibility problem
When you use the volatile keyword you have the following guarantees:
1. All writes to a volatile variable will be written also to the main memory immediately.
2. All reads to a volatile variable will come directly from the main memory.
3. All writes to a volatile variable are visible from all threads.
So how do you declare a volatile variable? See below for an example declaration.
public volatile int var1 = 0;
Let us see an actual Java program with and without the volatile keyword and explain the implications.
The image above shows 2 classes, VolatileClass and VolatileMainClass. Let us see VolatileMainClass first. In the VolatileMainClass, we have a main method. In the main method, we instantiate once the VolatileClass and then we run the start method. The start method will start the run method of the VolatileClass in his own thread. The next group of statements will ask the user to press the return key to stop the program, and when the user press return key the program terminates.
The VolatileClass is the class that is going to be run in a separate thread. The class has a non-volatile variable called working. The variable called working is a boolean, so it contains either true or false. It is initialized with true. Next, we have the run method. In this program, the run method will run an infinite loop. The loop will display a message, then it will be paused for three hundred milliseconds and iterate again. The message displayed will just read like “New Iteration number xx” where the xx is going to be substituted with the iteration number.
To stop the program we also have a method called terminateProgram. This method will change the value of the variable working to false. The infinite loop reads the value of the variable working and if that value is not true, it will terminate the loop.
Now let us analyze the variable called working in detail. The variable called working is not marked as volatile so the program thread does not have the visibility guarantees mentioned above. If this program is run in a JVM that copies the variable called working from main memory to a CPU cache, there is a possibility that the program will continue using the value in the cache and will never read the value from main memory. If the variable called working is changed from true to false in main memory, but its value in the CPU cache is true, and the JVM does not read the value of the main memory, then the loop will continue running forever.
Let us see how we solve the visibility problem of the program. By the way, let me mention that I ran the program in my computer without the volatile keyword and terminated it pressing the return key successfully many times. The program may work fine without the volatile keyword many times, but every once in a while might fail. If we add the volatile keyword we are guaranteeing that the program will always work.
Notice in the image below in line 10 we added the keyword volatile. By adding the volatile keyword we guarantee the visibility of the variable called working.
Other problems besides visibility
The guarantees provided by the volatile keyword are enough in many situations, but there are some situations when using volatile keyword is not enough. Let us look at a particular situation. Thread1 reads variable var1 with the value of 0 from main memory into CPU1 cache and increments the value of CPU1 cache var1 to 1.
Main memory var1 = 0.
CPU1 cache var1 = 1.
Thread2 reads variable var1 with the value of 0 from main memory and copies var1 to CPU2 cache, then increments var1 in CPU2 cache by 1. Now we have:
Main memory var1 = 0.
CPU1 cache var1 = 1.
CPU2 cache var1 = 1.
The threads are out of sync. Main memory var1 should be 2 because of the increments from Thread1 and Thread2.
The synchronized keyword
The synchronized keyword is used to avoid the scenario mentioned in the previous section. When you use the synchronized keyword you are making the reading and writing of variables an atomic operation. Synchronize keyword guarantees that a block of code is executed by only one thread, and the other threads must wait until the executing thread finishes. The Synchronized keyword will be discussed in detail in another article
In this article, we discussed the thread visibility problems that occur sometimes with non-volatile variables. We explain scenarios and solutions to the problem. We saw practical examples in Java programs, and we mentioned the synchronized keyword that goes a little bit further than the volatile keyword.