Some implementation may find this convenient to divide a single write action onto a 64-bit long or double value into two write action on adjacent 32-bit values. For efficient sake, this behavior is implementation specific
JVM’s are free to perform write to long and double values atomically or in 2 parts. From Java Memory Model point of view a single write to a non-volatile long or double value is treated as two separate write: one for each 32-bits half. This can result into situation where threads see the first 32-bits of a 64-bit value from one write and the second 32-bit from another write.
- Write to a volatile long and double values are always atomic.
- Write to and read of a reference are always atomic; regardless of whether they are implemented as 32-bit or 64-bit
declare shared 64-bit values as volatile or synchronize their read/write.
- 17.7 Non-atomic Treatment of double and long
Locks offer two primary features:
1. mutual exclusion, and
Mutual exclusion means that only one thread at a time may hold a given lock, and this property can be used to implement protocols for coordinating access to shared data such that only one thread at a time will be using the shared data.
Visibility is more subtle and has to do with ensuring that changes made to shared data prior to releasing a lock are made visible to another thread that subsequently acquires that lock — without the visibility guarantees provided by synchronization, threads could see stale or inconsistent values for shared variables.
Volatile variables share the visibility features of synchronized, but none of the atomicity features. This means that threads will automatically see the most up-to-date value for volatile variables. They can be used to provide thread safety, but only in a very restricted set of cases: those that do not impose constraints between multiple variables or between a variable’s current value and its future values.