28

How do I make an array volatile? Because as I've come to understand, it's unsafe to make an array volatile?

1

7 Answers 7

25

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)

Sign up to request clarification or add additional context in comments.

4 Comments

I don't think that the elements being volatile is the same as the elements' access being atomic.
You're right, volatile is about guaranteeing the happens-before relationship.
I really don't think this is a solution to the problem. The issue is (in part, as far as I understand) cache coherency, as well as the almost total lack of strong memory ordering semantics in the definition of the bytecode system and the JVM itself. Rewriting the array reference will not guarantee you are coherently flushing whichever array elements you wrote to cache or memory, so that all threads have the same view of the elements.
@LukeHutchison Actually I think it is possibly correct to do this (though really unelegant). I would rather cioy the volatile array field to a private variable every time though (privArr = volatileArr; then do something with privArr). Any volatile access ensures happens-before ordering of all other accesses. Howevr, what you will not get with this hack is atomic updates.
14

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

13

AtomicLongArray, AtomicIntegerArray, AtomicReferenceArray (java.util.concurrent.atomic).

Comments

3

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

Interesting idea.
3

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

2

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, where MyType has a volatile Data myData field. Then arr[3].myData has volatile behavior, because it's literally volatile

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

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.