Atomic array classes and volatile arrays in the JVM
Declaring an array volatile does not do what many think it does!
The common, simplistic understanding of the volatile keyword in Java is that if a variable is being modified by multiple threads, it should be marked volatile so that changes can be seen by other threads. (There is more to it that I’ll leave for a future post.)
If you had something like a shared boolean primitive marked volatile, this would work fine. However, when an array is marked volatile, it is only the array reference that is marked volatile! The contents of array itself do not inherit the memory visibility semantics of the volatile keyword at all.
So what you have is not an array of volatile primitives. Instead, you have a volatile array of non-volatile primitives.
This means that even if you declare an array to be volatile, modifying the contents of that array does not guarantee that those modifications will be seen by a different thread reading the contents of the array. However, if you update the reference to point to a new array object – this new array will correctly be seen by threads reading the reference. (No JVM guarantee about seeing changes to the contents of the new array though!)
So what is the correct way to share contents of a mutable array with multiple threads? Use the Atomic*Array classes in the java.util.concurrent.atomic package.
AtomicLongArray, AtomicIntegerArray, AtomicReferenceArray<V> are your friends.
“No AtomicFloatArray and AtomicDoubleArray!”, you exclaim. Well, recall that a float is 32 bits and so is an integer. A double is 64 bits and so is a long. Use the Double.doubleToLongBits and Float.floatToIntBits methods to your advantage. Simple, isn’t it?
An alternative way to get volatile semantics for an array of primitives is to wrap the primitive with a wrapper that declares the primitive volatile. Accessing the volatile field from the array of wrappers will give you the desired volatile semantics.
What about an array of AtomicReference<V> variables? (AtomicReference<V> array;) – Yes, this is going to work too. But you should probably use AtomicReferenceArray<V> instead. It uses less memory which can be significant if you have millions of values in the array.
There is more to volatile – but that’s for another post.
Tags: Java, Java Memory Model, JLS, JMM, JVM