но это не работает. Возможно ли вообще?
Ответ 3
Еще один способ сделать это - использовать класс JDK 9+ VarHandle
. Как вы можете видеть в исходном коде классов Atomic
xxx Array
, таких как 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
.
И затем вы можете получить к нему доступ, используя методы set
xxx (array, index, value)
и get
xxx (array, index)
, где array
имеет тип yourArrayClass
, index
имеет тип int
, value
является типа элемента в вашем массиве (yourArrayClass.getComponentType()
).
Обратите внимание, что если, например, yourArrayClass == byte[].class
, но вы вводите 42
как value
, вы получаете ошибку, потому что 42
является int
вместо byte
, а параметры методов доступа - vararg 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 Unsafe
все еще используется в AtomicInteger
из-за "неразрешенных циклических зависимостей запуска".
Если вы хотите узнать больше о различных доступных методах get и set, посмотрите Использование режимов упорядочения памяти JDK 9 (я должен сказать, что я совсем не эксперт в этом (пока? )).
Обратите внимание, что на сегодняшний день вы не можете использовать VarHandle
в Kotlin, потому что он упаковывает параметры vararg Object...
методов get и set в Object[]
, см. ошибка KT-26165:
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,Object[])void
(необходимо исправить сейчас)