37

有没有办法volatile在 Java 中声明数组元素?IE

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

声明了数组引用 volatile,但数组元素(例如a[1])仍然不是易失性的。所以我正在寻找类似的东西

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

但它不是那样工作的。有可能吗?

4

4 回答 4

32

使用AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray

该类AtomicIntegerArray实现了一个 int 数组,其各个字段可以通过类的get()set()方法使用 volatile 语义访问。然后从一个线程调用将arr.set(x, y)保证另一个线程调用arr.get(x)将读取值 y(直到另一个值被读取到位置 x)。

看:

于 2010-02-10T10:56:39.140 回答
6

不,您不能使数组元素易失。另请参阅http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.html

于 2010-02-10T10:58:11.807 回答
6

另一种方法是使用 JDK 9+VarHandle类。Atomic正如您在xxxArray类的源代码中所见AtomicIntegerArray,这些类也VarHandle从 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);
}

//[...]

你首先创建一个VarHandle这样的:

MethodHandles.arrayElementVarHandle(yourArrayClass)

例如,您可以byte[].class在此处输入自己实现缺失AtomicByteArray

然后您可以使用setxxx(array, index, value)getxxx(array, index)方法访问它,其中array是 type yourArrayClassindex是 type intvalue是数组中元素的类型 ( yourArrayClass.getComponentType())。

请注意,例如,如果yourArrayClass == byte[].class您输入42as value,则会收到错误,因为42is anint而不是 abyte并且访问方法的参数是可变Object...参数:

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

(第二个签名是您使用的签名,第一个签名是您应该使用的签名。)


请注意,在 JDK 8 及以下版本sun.misc.Unsafe中用于实现原子类,例如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);
}

//[...]

UsingUnsafe仍然是一种选择(尽管我认为获取实例有点棘手),但不鼓励这样做,因为您必须自己检查数组边界,如果您犯了错误,它可能会导致 Java 进程出现段错误,而VarHandle边界会为您检查如果给定的索引超出范围,则抛出 Java 异常(但这可能会带来性能成本)。除此之外,Unsafe不受官方支持,可能随时被删除。

但是由于'unresolved cyclic startup dependencies'Unsafe仍然使用JDK 10 。AtomicInteger


如果您想了解有关可用的不同 get 和 set 方法的更多信息,请查看Using JDK 9 Memory Order Modes(我不得不说我根本不是这方面的专家(还没有?))。


请注意,从今天开始,您不能VarHandle在 Kotlin 中使用,因为它将Object...get 和 set 方法的 vararg 参数包装在 an 中Object[],请参阅错误 KT-26165

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

(现在应该修复)

于 2018-08-16T11:12:56.270 回答
0

这个怎么样:

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;

单元格也会使内容不稳定。我也仅在预先分配单元后才将新数组分配给易失性数组...存在 Cell 额外内存的权衡,但它是可管理的

于 2019-03-06T10:13:56.083 回答