38

Is there a way to declare array elements volatile in Java? I.e.

volatile int[] a = new int[10];

declares the array reference volatile, but the array elements (e.g. a[1]) are still not volatile. So I'm looking for something like

volatile int[] a = new volatile int[10];

but it doesn't work that way. Is it possible at all?

Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169
  • @Kanagavelu Sugumar: AtomicReference _is_ a wrapped `volatile`, with some extra methods (getAndSet etc.). – Joonas Pulakka Mar 11 '15 at 07:54
  • Yes you can make array volatile. Please refer this - http://javarevisited.blogspot.in/2015/10/133-java-interview-questions-answers-from-last-5-years.html – reactdontact May 02 '18 at 16:17

4 Answers4

33

Use AtomicIntegerArray or AtomicLongArray or AtomicReferenceArray

The AtomicIntegerArray class implements an int array whose individual fields can be accessed with volatile semantics, via the class's get() and set() methods. Calling arr.set(x, y) from one thread will then guarantee that another thread calling arr.get(x) will read the value y (until another value is read to position x).

See:

uthark
  • 5,333
  • 2
  • 43
  • 59
  • I wonder why there are specific `AtomicArrays` for int and long, but not for other primitive types... But of course the rest of the primitives could be faked by using their wrappers in an `AtomicReferenceArray`. – Joonas Pulakka Feb 10 '10 at 11:26
  • I think AtomicIntegerArray and AtomicLongArray are optimized to work with integer and long respectively. – uthark Feb 10 '10 at 11:44
  • 4
    @JoonasPulakka for other primitive types you can also convert them to int or long using e.g. `Float.floatToIntBits(float)`. This avoids the need of boxing when using `AtomicReferenceArray`. – Didier L May 02 '15 at 19:53
7

No, you can't make array elements volatile. See also http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.html .

Tim Jansen
  • 3,330
  • 2
  • 23
  • 28
  • Actually you can, but with additional efforts. – uthark Feb 10 '10 at 11:00
  • Technically that's still not making the elements volatile, but the array operations are volatile. Since `int` for this case is a primitive, it essentially has the same behavior, but this could not be easily extended for non-primitive arrays. – Marcus Jan 14 '15 at 21:59
6

Another way to do this is using the JDK 9+ VarHandle class. As you can see in the source code of the AtomicxxxArray classes like AtomicIntegerArray, these classes also use VarHandle since JDK 9:

//[...]

private static final VarHandle AA
    = MethodHandles.arrayElementVarHandle(int[].class);
private final int[] array;

//[...]

/**
 * Returns the current value of the element at index {@code i},
 * with memory effects as specified by {@link VarHandle#getVolatile}.
 *
 * @param i the index
 * @return the current value
 */
public final int get(int i) {
    return (int)AA.getVolatile(array, i);
}

/**
 * Sets the element at index {@code i} to {@code newValue},
 * with memory effects as specified by {@link VarHandle#setVolatile}.
 *
 * @param i the index
 * @param newValue the new value
 */
public final void set(int i, int newValue) {
    AA.setVolatile(array, i, newValue);
}

//[...]

You first create a VarHandle like this:

MethodHandles.arrayElementVarHandle(yourArrayClass)

For example, you can enter byte[].class here to implement the missing AtomicByteArray yourself.

And then you can access it using the setxxx(array, index, value) and getxxx(array, index) methods, where array is of type yourArrayClass, index is of type int, value is of the type of an element in your array (yourArrayClass.getComponentType()).

Note that if, for example, yourArrayClass == byte[].class but you enter 42 as value, you get an error because 42 is an int instead of a byte and the parameters of the access methods are vararg Object... parameters:

java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,byte[],int,int)void

(The second signature is the one that you used, the first one is the one that you should have used.)


Note that in JDK 8 and below sun.misc.Unsafe was used to implement the atomic classes like AtomicIntegerArray:

//[...]

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int shift;
private final int[] array;

static {
    int scale = unsafe.arrayIndexScale(int[].class);
    if ((scale & (scale - 1)) != 0)
        throw new Error("data type scale not a power of two");
    shift = 31 - Integer.numberOfLeadingZeros(scale);
}

private long checkedByteOffset(int i) {
    if (i < 0 || i >= array.length)
        throw new IndexOutOfBoundsException("index " + i);

    return byteOffset(i);
}

private static long byteOffset(int i) {
    return ((long) i << shift) + base;
}

//[...]

/**
 * Gets the current value at position {@code i}.
 *
 * @param i the index
 * @return the current value
 */
public final int get(int i) {
    return getRaw(checkedByteOffset(i));
}

private int getRaw(long offset) {
    return unsafe.getIntVolatile(array, offset);
}

/**
 * Sets the element at position {@code i} to the given value.
 *
 * @param i the index
 * @param newValue the new value
 */
public final void set(int i, int newValue) {
    unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}

//[...]

Using Unsafe is still an option (although I think it's a bit tricky to obtain an instance), but it is discouraged because you have to check array bounds yourself and it might segfault the Java process if you make a mistake, while VarHandle does bounds checks for you and throws a Java exception if the given index is out of bounds (but that may come with a performance cost). Besides that, Unsafe is not officially supported and might be removed at any time.

But as of JDK 10 Unsafe is still used in AtomicInteger because of 'unresolved cyclic startup dependencies'.


If you want to know more about the different get and set methods available, take a look at Using JDK 9 Memory Order Modes (I have to say that I'm not an expert in this at all (yet?)).


Note that as of today you cannot use VarHandle in Kotlin, because it wraps the vararg Object... parameters of the get and set methods in an Object[], see bug KT-26165:

java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,Object[])void

(should be fixed now)

SWdV
  • 1,715
  • 1
  • 15
  • 36
0

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