Java’s Volatile Modifier
A while ago I wrote a Java servlet Filter that loads configuration in its init
function (based on a parameter from web.xml
). The filter’s configuration is cached in a private field. I set the volatile modifier on the field.
When I later checked the company Sonar to see if it found any warnings or issues in the code I was a bit surprised to learn that there was a violation on the use of volatile. The explanation read:
Use of the keyword ‘volatile’ is generally used to fine tune a Java application, and therefore, requires a good expertise of the Java Memory Model. Moreover, its range of action is somewhat misknown. Therefore, the volatile keyword should not be used for maintenance purpose and portability.
I would agree that volatile is misknown by many Java programmers. For some even unknown. Not only because it’s never used much in the first place, but also because it’s definition changed since Java 1.5.
Let me get back to this Sonar violation in a bit and first explain what volatile means in Java 1.5 and up (until Java 1.8 at the time of writing).
What is Volatile?
While the volatile modifier itself comes from C, it has a completely different meaning in Java. This may not help in growing an understanding of it, googling for volatile could lead to different results. Let’s take a quick side step and see what volatile means in C first.
In the C language the compiler ordinarily assumes that variables cannot change value by themselves. While this makes sense as default behavior, sometimes a variable may represent a location that can be changed (like a hardware register). Using a volatile variable instructs the compiler not to apply these optimizations.
Back to Java. The meaning of volatile in C would be useless in Java. The JVM uses native libraries to interact with the OS and hardware. Further more, it is simply impossible to point Java variables to specific addresses, so variables actually won’t change value by themselves.
However, the value of variables on the JVM can be changed by different threads. By default the compiler assumes that variables won’t change in other threads. Hence it can apply optimizations such as reordering memory operations and caching the variable in a CPU register. Using a volatile variable instructs the compiler not to apply these optimizations. This guarantees that a reading thread always reads the variable from memory (or from a shared cache), never from a local cache.
Atomicity
Further more on a 32 bit JVM volatile makes writes to a 64 bit variable atomic (like long
or double
). To write a variable the JVM instructs the CPU to write an operand to a position in memory. When using the 32 bit instruction set, what if the size of a variable is 64 bits? Obviously the variable must be written with two instructions, 32 bits at a time.
In multi-threaded scenarios another thread may read the variable half way through the write. At that point only first half of the variable is written. This race-condition is prevented by volatile, effectively making writes to 64 bit variables atomic on 32 bit architectures.
Note that above I talked about writes not updates. Using volatile won’t make updates atomic. E.g. ++i
when i
is volatile would read the value of i
from the heap or L3 cache into a local register, inc
that register, and write the register back into the shared location of i
. In between reading and writing i
it might be changed by another thread. Placing a lock around the read and write instructions makes the update atomic. Or better, use non-blocking instructions from the atomic variable classes in the concurrent.atomic
package.
Side Effect
A volatile variable also has a side effect in memory visibility. Not just changes to the volatile variable are visible to other threads, but also any side effects of the code that led up to the change are visible when a thread reads a volatile variable. Or more formally, a volatile variable establishes a happens-before relationship with subsequent reads of that variable.
I.e. from the perspective of memory visibility writing a volatile variable effectively is like exiting a synchronized block and reading a volatile variable like entering one.
Choosing Volatile
Back to my use of volatile to initialize a configuration once and cache it in a private field.
Up to now I believe the best way to ensure visibility of this field to all threads is to use volatile. I could have used AtomicReference
instead. Since the field is only written once (after construction, hence it cannot be final) atomic variables communicate the wrong intent. I don’t want to make updates atomic, I want to make the cache visible to all threads. And for what it’s worth, the atomic classes use volatile too.
Thoughts on this Sonar Rule
Now that we’ve seen what volatile means in Java, let’s talk a bit more about this Sonar rule.
In my opinion this rule is one of the flaws in configurations of tools like Sonar. Using volatile can be a really good thing to do, if you need shared (mutable) state across threads. Sure thing you must keep this to a minimum. But the consequence of this rule is that people who don’t understand what volatile is follow the recommendation to not use volatile. If they remove the modifier effectively they introduce a race-condition.
I do think it’s a good idea to automatically raise red flags when misknown or dangerous language features are used. But maybe this is only a good idea when there are better alternatives to solve the same line of problems. In this case, volatile has no such alternative.
Note that in no way this is intended as a rant against Sonar. However I do think that people should select a set of rules that they find important to apply, rather than embracing default configurations. I find the idea to use rules that are enabled by default, a bit naive. There’s an extremely high probability that your project is not the one that tool maintainers had in mind when picking their standard configuration.
Furthermore I believe that as you encounter a language feature that you don’t know, you should learn about it. As you learn about it you can decide if there are better alternatives.
Java Concurrency in Practice
The de facto standard book about concurrency in the JVM is Java Concurrency in Practice by Brain Goetz. It explains the various aspects of concurrency in several levels of detail. If you use any form of concurrency in Java (or impure Scala) make sure you at least read the former three chapters of this brilliant book to get a decent high-level understanding of the matter.
Reference: | Java’s Volatile Modifier from our JCG partner Bart Bakker at the Software Craft blog. |
Bart, Indeed a good article and I would agree on “googling for volatile could lead to different results” and actually it has different explanation for C and Java ….and there starts the confusion. Do you have some references to understand volatile in java , as its still an open (or open ended) question to me. Can you please help me understand a conflict as per your article , as java spec says “Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values.” Please see the JLS 3ed, Section 17.7,[http://docs.oracle.com/javase/specs/jls/se5.0/html/memory.html#17.7]… Read more »
Hi Sankalp, Thanks for your response. Glad you enjoyed my article. The conflict as you mention does not really exist. There’s a difference between values and references. You can use 64 bit values (long and double) on any architecture. However, 64 bit references can only be used on a 64 bit architecture. A 64 bit JVM uses the 64 bit assembly instruction set, hence reads and writes of 64 bit references are atomic. On 32 bit architectures a 64 bit value must be read and written with 2 instructions. I.e. reads and writes are not atomic. Volatile fixes this. Brian… Read more »