Volatile in Java and C/C++
Volatile in Java and C/C++
The volatile
keyword is available in many programming languages, including C, C++, and Java.
But the C/C++ and Java volatile
keywords have different semantics.
Reordering
On modern platforms, code is frequently not executed in the order it was written. It is reordered by the compiler, the processor, and the memory subsystem to achieve maximum performance. On multiprocessor architectures, individual processors may have their local caches that are out of sync with the main memory. It is generally undesirable to require threads to remain perfectly in sync with one another because this would be too costly from a performance point of view. This means that at any given time, different threads may see different values for the same shared data. reference: wikipedia.
Reordering means the reader thread may see those writes in an order other than the actual program order. For example, thread(1) writes x=1,y=2
sequentially, but after threads(2) see x=1
, the variable y
may have not changed.
Memory Visibility
The writes may be seen by the other processor, because individual processors may have their local caches that are out of sync with the main memory.
When thread(1) updates x=0
to x=1
, there are no guarantees about what the other threads may see. In other words, the other threads may see x=0
after thread(1) is updated.
The Java volatile
The Java volatile
keyword is a way to control memory order
. It provides memory visibility between multiple threads and prevents reordering. It guarantees the happens-before
relationship between the writer and the reader.
Let’s take an example.
class Example {
private static int number;
private static volatile boolean ready
}
The writer thread does the following instructions below.
Example.ready = true;
Example.number = 1;
The reader thread will get the correct number
after the ready
was set to true
in the writer thread because of the strength of happens-before
memory ordering and the memory visibility of volatile
.
while (!Example.ready) {
Thread.yield();
}
System.out.println(number); // output: 1
The C/C++ volatile
The volatile in C/C++ is not guaranteed the memory order of the modified variable, it just tells the compiler not to optimize the variable since the variable may be changed unexpectedly:
- memory-mapped I/O devices
- uses of variables between
setjmp
andlongjmp
- uses of
sig_atomic_t
variables in signal handlers.
Operations on volatile variables are not atomic, nor do they establish a proper happens-before
relationship for threading.
The internal of Java volatile
The Java volatile
is similar to C11 (C++11) memory_order
. It use memory_barrier
to guarantee happens-before
semantics.
The reading of Java volatile
variable acts as acquire
semantics.
- equivalent to
std::memory_order_acquire
. - A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load. All writes in other threads that release the same atomic variable are visible in the current thread. reference cpp reference.
The writing of Java volatile
variable acts as release
semantics.
- equivalent to
std::memory_order_release
. - A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable, and writes that carry a dependency into the atomic variable become visible in other threads that consume the same atomic. reference cpp reference.
In the x86
and amd64
architecture, the std::memory_order_acquire
and std::memory_order_release
only need to prevent compiler reordering since the MOV
instruction guarantees the acquire
memory order on reading and release
memory ordering on writing.
Conclusion
The C/C++ and Java volatile
keywords have completely different semantics. The C/C++ volatile
keyword just tells the compiler not to optimize the variable since the variable may be changed unexpectedly. The Java volatile
is more like a C/C++ atomic operation, and provides happens-before
semantics.