26

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

Michael
  • 525
  • 2
  • 6
  • 6

4 Answers4

24

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)

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
nicolaas
  • 1,783
  • 1
  • 17
  • 24
  • 2
    I don't think that the elements being volatile is the same as the elements' access being atomic. – Blindy Mar 02 '11 at 21:26
  • 1
    You're right, `volatile` is about guaranteeing the happens-before relationship. – mike Aug 09 '13 at 10:55
  • 3
    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. – Luke Hutchison Oct 27 '19 at 02:46
  • @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. – Stefan Reich Jun 16 '22 at 21:11
12

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 ...

PeMa
  • 1,559
  • 18
  • 44
markus
  • 511
  • 1
  • 4
  • 15
11

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

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
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

gotch4
  • 13,093
  • 29
  • 107
  • 170