How do I make an array volatile? Because as I've come to understand, it's unsafe to make an array volatile?
7 Answers
Declaring an array volatile does not give volatile access to its fields. You're declaring the reference itself volatile, not its elements.
In other words, you're declaring a volatile set of elements, not a set of volatile elements.
The solution here is to use AtomicIntegerArray in case you want to use integers. Another way (but kinda ugly) is to rewrite the reference to the array every time you edit a field.
You do that by:
arr = arr;
(as I said... ugly)
4 Comments
volatile is about guaranteeing the happens-before relationship.EDIT: Arrays are objects in java. If you make the reference to that object volatile, makes it visible to other threads if you exchange the reference to the array. However this does not hold for the array values themselves.
Getting a better understanding of the java memory model, there is actually a possibility to get around it without an Atomic*Array. Using the happened-before relationship for volatile reads and normal writes makes it possible:
If thread A writes some non-volatile stuff and a volatile variable after that, thread B is guaranteed to see the changes of the non-volatile stuff as well, but only if thread B reads the volatile variable first. see also: Happens-before relationships with volatile fields and synchronized blocks in Java - and their impact on non-volatile variables?
For arrays, this means: After you write to the array, write to some volatile status variable (make sure the write actually changes the volatile status variable!) When you read from the array, read the volatile status variable first, and then access the array. The volatile read should make all other writes visible as well, as long as they happened before.
OLD:
writing the self reference arr=arr wouldn't actually help.
You write the address of the array arr, not the value of the field arr[i]. So you still gain no volatile properties for arr[i] (which you want), but only for the storage address arr.
The previously mentioned blogpost of Jeremy Manson explains it in detail: http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.html
His best solution is to use Atomic*Arrays, namely the AtomicReferenceArray for generic types (there also exist special forms for basic types). I can't imagine that this is particularly efficient, especially as it gains you more properties that you need (atomicity >> volatile).
An alternative may be pointered structures where the containers use volatile pointer fields. Also not that efficient ...
Comments
How about this:
static class Cell<T> {
volatile T elem;
}
private Cell<T>[] alloc(int size){
Cell<T>[] cells = (Cell<T>[]) (new Cell[size]);
return cells;
}
volatile Cell<T>[] arr;
Cell<T>[] newarr = alloc(16);
for (int i = 0; i < newarr.length; i++) {
newarr[i] = new Cell<>();
}
arr = newarr;
the cells make the content volatile too. also I assign the new array to the volatile one only after pre allocating the cells... there's the trade off of the Cell extra memory, but it's manageable
1 Comment
Java9 introduced a generic approach to deal with volatile arrays: VarHandle, allowing atomic operations on any type of array.
Here is a self-descriptive example from the doc:
String[] sa = ...
VarHandle avh = MethodHandles.arrayElementVarHandle(String[].class);
boolean r = avh.compareAndSet(sa, 10, "expected", "new");
Might worth to note as well, that atomic types from "concurrent" package (like AtomicInteger, AtomicIntegerArray, AtomicReference, etc.) since Java9 are using VarHandle instead of Unsafe to implement platform-dependent operations like "CompareAndSet".
Comments
Here I offer another approach for making array elements have volatile behavior that doesn't involve Atomic<TypeName> or wrappers of volatile fields.
To make the array itself volatile, you only need to use volatile MyType[] arr. That makes arr volatile, which in technical terms, means that all writes to arr itself have a "happens before" relationship to all later reads of arr, across threads. This is totally safe despite warnings and conventional widsom to the contrary.
The compiler and CPU are normally free to reorder writes and reads to arr arbitrarily if it doesn't change the results of single-threaded execution. See the Java 21 spec, bottom of page 727. But when something is marked volatile, all writes to the volatile variable must happen before all later reads across threads as well. See the same spec, middle of page 728.
What people commonly say about volatile, like that it "prevents caching" and "guarantees that writes really happen" are true... but they kind miss the point. The key thing is that there's the happens-before relationship. Everything else are the logical consequences that must be true to adhere to the Java specification.
Note: writes to arr/reads from arr specifically mean arr itself, not its entries.
This is is why volatile arrays are conventionally considered "not safe." The array itself being volatile is fine. But the bugs show up when people treat myVolatileArr[3] as being volatile. It isn't!
The other answers have good and "safe," i.e. not sun.misc.Unsafe, ways of providing volatile semantics to array elements. To summarize, the ideas are
- use
AtomicLong, etc. These type have volatile behavior - use
MyType[] arr, whereMyTypehas avolatile Data myDatafield. Thenarr[3].myDatahas volatile behavior, because it's literallyvolatile
Here's my real contribution: "unsafe" operations. If performance and lack of JVM overhead matters, you can use the infamous sun.misc.Unsafe. In particular, Unsafe.getReferenceVolatile(arr, i) will access arr[i] as an Object with volatile semantics. If you have an array of primitives, Unsafe also has methods such as Unsafe.getIntVolatile to get such elements.
"Unsafe" is kind of a scare word; it's more of a warning than a true sign of danger. It
- allows you to manipulate memory outside the GC
- is not protected with the same level guardrails present elsewhere
- heavily relies on intrinsics, whose implementations vary across platforms, and may therefore expose you to subtle platform-dependent gotchas... the kind that Java was created to avoid in the first place!
Nevertheless, Unsafe is widely used at low levels in libraries because of the intrinsics and lack of guardrails. Guardrails and standardized behavior are constraints, and constraints limit maximum performance.
Comments
You really should read this : java volatile array,My test results do not match the expectations
As it has been tested, any read to any volatile variable seems to retrieve the most updated values.
Keep in mind that it could depend on the JVM implementation.