When to use Volatile over Synchronized modifiers can be summed up into this:
- Use Volatile when you variables are going to get read by multiple threads, but written to by only one thread.
- Use Synchronized when your variables will get read and written to by multiple threads.
The reasons for that are very well explained at Jenkov.com so I will discuss them briefly.
When running multi threaded application on multi CPU hardware it’s possible that 2 Java threads can execute in 2 different CPU threads. The CPU threads have their own CPU Thread Cache (a.k.a Java Stack Cache) that is used by the JVM to copy over variables from the Main Memory, obviously for performance reasons since access to Main Memory is very costly. And here lies the issue! There is no guarantee when the updated variable is going to get flushed back to Main Memory for other threads to access.
|It’s important to understand that there are two aspects to thread safety: (1) execution control, and (2) memory visibility. The first has to do with controlling when code executes (including the order in which instructions are executed) and whether it can execute concurrently, and the second to do with when the effects in memory of what has been done are visible to other threads.|
Use Volatile when you variables are going to get read by multiple threads, but written to by only one thread
The below diagram shows two threads: Thread 1 and Thread 2. If Thread 1 will read and write to a common “counter” variable it will:
- read it from the Main Memory into CPU cache;
- write to it in CPU Cache;
- flush the new value back to Main Memory;
Since only one thread is operating on that variable – all is fine.
However, if Thread 2 needs to also read that variable from the Main Memory it may end up with the old, non-incremented value, because there is no guarantee when the Thread 1 will flush it’s update. This represents memory visibility issue – the updates of one thread are not visible to other threads.
The situation can be quickly resolved by using Volatile modifier on the “counter” variable. Volatile will force all changes made by this thread to the “counter” variable in the CPU Cache to be immediately flushed back to the Main Memory so other threads can always access fresh data, effectively keeping the volatile variable out of CPU caches.
Volatile should not be used when multiple threads can read and write to shared variables, because though any changes made by Thread 1 will be flushed back to Main Memory, the flushing will still take time and that creates racing condition which may at some point ruin “counter” ‘s value.
Use Synchronized when your variables will get read and written to by multiple threads
When both Thread 1 and Thread 2 need to read and write the “counter” variable Volatile is not enough to guarantee predictable code execution. This represents execution control issue.
In that case it’s best to use Synchronized modifier that will control access to the counter “variable”. Synchronized will flush any changes made in the CPU Cache to the Main Memory when current thread releases it’s lock. That way any following thread will always see fresh data.
Knowing that Volatile variables will always read and write to the Main Memory takes care of any concerns about the Volatile variable itself. But what about the other variables that were changes before the volatile one? Second thread may need them as well. Declaring them Volatile may seem like an answer, but will pollute a lot of code. Fortunately that wouldn’t be necessary, because since Java 5 the Volatile modifier got optimized for that exact situation.
After Java 5, all changes to variables made by a thread will be flushed back to Main Memory each time a volatile field is changed or read. This is called “Happen-Before” behavior. It basically means that all changes to other variables that happen before the line of execution hits a volatile variable will be flushed back to the Main Memory. The same thing happens when the line of execution hits Synchronized block. That is why Volatile and Synchronized are considered to be almost the same.
Performance Pros and Cons
Volatile does not control thread access to its object which allows multiple threads to operate on it without the need to wait for object’s lock – very fast performance. And it will make sure that any changes to the variables are going to get flush back to Main Memory.
However, there is the overhead of reading and writing directly to the Main Memory, because access to the Main Memory is considered to be very costly. Also in order to assure that changes to a Volatile variable will be flushed back to Main Memory along with changes to all affected variables, the JVM is not going to perform instruction reordering on those lines of code. This is bad because instruction reordering is an automated process that is meant to optimize code for faster native execution.
Synchronized is perfect for thread access control. It guarantees that a piece of code will be accessed by no more than one thread at a time, eliminating all possible race conditions and other concurrency issues.
However, it’s very expensive to use, because the synchronized piece of code looses the most important multi-threaded advantage – speed.
Links & Resources
Difference between volatile and synchronized in JAVA – StackOverflow
Java Volatile Keyword – tutorials.jenkov.com