有没有办法在Java中声明数组元素volatile
?即
volatile int[] a = new int[10];
声明数组引用volatile
,但数组元素(例如a[1]
)仍然不是volatile。所以我正在寻找类似的东西
volatile int[] a = new volatile int[10];
但它不会那样工作。有可能吗?
使用AtomicIntegerArray
或AtomicLongArray
或AtomicReferenceArray
AtomicIntegerArray
类实现了一个int数组,通过类的get()
和set()
方法,可以使用volatile语义访问各个字段。从一个线程调用arr.set(x, y)
将保证调用arr.get(x)
的另一个线程将读取值y(直到另一个值被读取到位置x)。
看到:
另一种方法是使用JDK 9+ VarHandle
类。正如您在Atomic
xwixixxxArray
类的源代码中所看到的那样,这些类也从JDK 9开始使用AtomicIntegerArray
:
VarHandle
你首先像这样创建一个//[...]
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
xxxset
和(array, index, value)
xxxget
方法访问它,其中(array, index)
的类型为array
,yourArrayClass
的类型为index
,int
是你的数组中元素的类型(value
)。
请注意,如果,例如,yourArrayClass.getComponentType()
,但你输入yourArrayClass == byte[].class
作为42
,你得到一个错误,因为value
是一个42
而不是int
,访问方法的参数是vararg byte
参数:
Object...
(第二个签名是您使用的签名,第一个签名是您应该使用的签名。)
请注意,在JDK 8及以下版本中,java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,byte[],int,int)void
用于实现像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);
}
//[...]
仍然是一个选项(虽然我认为获取实例有点棘手),但是不鼓励它因为你必须自己检查数组边界,如果你犯了错误它可能会破坏Java进程,而Unsafe
会进行边界检查对于你,如果给定的索引超出范围,则抛出Java异常(但这可能带来性能成本)。除此之外,VarHandle
不受官方支持,可能随时被删除。
但是从JDK 10开始,由于'未解决的循环启动依赖性',Unsafe
仍在Unsafe
中使用。
如果你想了解更多关于不同的get和set方法的信息,请看看AtomicInteger
(我必须说我根本就不是这方面的专家(还是?))。
请注意,截至今天你不能在Kotlin中使用Using JDK 9 Memory Order Modes,因为它包含了VarHandle
中get和set方法的vararg Object...
参数,请参阅Object[]
:
bug KT-26165
这个怎么样:
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,Object[])void
细胞也使内容易变。我也只是在预先分配了单元格之后才将新数组分配给volatile数组...还有Cell额外内存的权衡,但它是可管理的