另一种方法是使用 JDK 9+ VarHandle 类。正如您在 AtomicxxxArray 类的源代码中看到的,如 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 的类型为@ 987654343@,index 是 int 类型,value 是数组中元素的类型 (yourArrayClass.getComponentType())。
请注意,例如,如果 yourArrayClass == byte[].class 但您将 42 输入为 value,则会出现错误,因为 42 是 int 而不是 byte 和访问方法的参数是可变参数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);
}
//[...]
使用Unsafe 仍然是一种选择(虽然我认为获取实例有点棘手),但不鼓励这样做,因为你必须自己检查数组边界,如果你犯了错误,它可能会导致 Java 进程出现段错误,而VarHandle 会为您进行边界检查并在给定索引超出范围时引发 Java 异常(但这可能会带来性能成本)。除此之外,Unsafe 不受官方支持,可能随时被删除。
但从 JDK 10 开始,AtomicInteger 中仍然使用 AtomicInteger,因为 '未解决的循环启动依赖关系'。
如果您想了解更多有关可用的不同 get 和 set 方法的信息,请查看 Using JDK 9 Memory Order Modes(我不得不说我根本不是这方面的专家(还没有?))。
请注意,从今天开始,您不能在 Kotlin 中使用 VarHandle,因为它将 get 和 set 方法的可变参数 Object... 包装在 Object[] 中,请参阅 bug KT-26165:
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,Object[])void
(现在应该修复)