From Goetz, Peierls, Bloch et al. 2006: Java Concurrency in Practice
3.1.2. Nonatomic 64-bit Operations
When a thread reads a variable without synchronization, it may see a stale value, but at least it sees a value that was actually placed there by some thread rather than some random value. This safety guarantee is called out-of-thin-air safety.
Out-of-thin-air safety applies to all variables, with one exception: 64-bit numeric variables ( double and long ) that are not declared volatile (see Section 3.1.4 ). The Java Memory Model requires fetch and store operations to be atomic, but for nonvolatile long and double variables, the JVM is permitted to treat a 64-bit read or write as two separate 32-bit operations. If the reads and writes occur in different threads, it is therefore possible to read a nonvolatile long and get back the high 32 bits of one value and the low 32 bits of another.[3]
Thus, even if you don't care about stale values, it is not safe to use shared mutable long and double variables in multithreaded programs unless they are declared volatile or guarded by a lock.
[3] When the Java Virtual Machine Specification was written, many widely used processor architectures could not efficiently provide atomic 64-bit arithmetic operations.
This was written after the release of Java 5 which came out in 2004, with many changes targetting easier multi-threading and concurrency programming. So why does it still apply? Even now a decade later?
If it is only because it is possible to run Java apps on 32-bit hardware, why isn't there a JVM run-time option to allow it if desired?
Wouldn't it be beneficial to be able to code multi-threaded and low latency applications without needing to worry about this gotcha?
Yes, Java is inherently multi-threaded. A single Java program can have many different threads executing independently and continuously. Three Java applets on the same page can run together with each getting equal time from the CPU with very little extra effort on the part of the programmer.
Multithreading is a Java feature that allows concurrent execution of two or more parts of a program for maximum utilization of CPU. Each part of such program is called a thread. So, threads are light-weight processes within a process. Threads can be created by using two mechanisms : Extending the Thread class.
Stack Memory and Threads Secondly, it stores local primitives and local object references on the stack. In addition, it's important to realize that every thread, including the main thread, has its own private stack. Therefore, other threads do not share our local variables, which is what makes them thread-safe.
Is this 64-bit numeric variable multi-threading rule still true in Java 1.8 or is it out-of-date?
Yes, it is still true, refer to the Java 8 Specification:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.
Writes and reads of volatile long and double values are always atomic.
Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
So why does it still apply?
The explanation is also in the specification:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7
Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes to long and double values atomically or in two parts.
Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.
I think this is because Java is running on many hardware models - for example mobile phones, routers, maybe even on refrigerators, washing machines, vacuums etc. etc., which can be equiped with tiny 8 bit or 16 bit microprocessors. And Java specification is common to all of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With