volatile
Javaで配列要素を宣言する方法はありますか?つまり
volatile int[] a = new int[10];
配列参照 を宣言しますvolatile
が、配列要素(たとえばa[1]
)はまだ揮発性ではありません。だから私は次のようなものを探しています
volatile int[] a = new volatile int[10];
しかし、それはそのようには機能しません。それは可能ですか?
volatile
Javaで配列要素を宣言する方法はありますか?つまり
volatile int[] a = new int[10];
配列参照 を宣言しますvolatile
が、配列要素(たとえばa[1]
)はまだ揮発性ではありません。だから私は次のようなものを探しています
volatile int[] a = new volatile int[10];
しかし、それはそのようには機能しません。それは可能ですか?
AtomicIntegerArray
またはを使用AtomicLongArray
するAtomicReferenceArray
このクラスは、クラスとメソッドAtomicIntegerArray
を介して、揮発性セマンティクスで個々のフィールドにアクセスできるint配列を実装します。あるスレッドから呼び出すと、別のスレッド呼び出しが値yを読み取ることが保証されます(別の値が位置xに読み取られるまで)。get()
set()
arr.set(x, y)
arr.get(x)
見る:
いいえ、配列要素を揮発性にすることはできません。http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.htmlも参照してください。
これを行う別の方法は、JDK9+VarHandle
クラスを使用することです。Atomic
のようなxxxArray
クラスのソースコードでわかるようAtomicIntegerArray
に、これらのクラスはVarHandle
JDK9以降でも使用されます。
//[...]
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
した場合、はaの代わりにであり、アクセスメソッドのパラメータはvarargパラメータであるvalue
ため、エラーが発生することに注意してください。42
int
byte
Object...
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,byte[],int,int)void
(2番目の署名は使用した署名であり、最初の署名は使用すべき署名です。)
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
公式にはサポートされておらず、いつでも削除される可能性があります。
ただし、 「未解決の循環スタートアップ依存関係」のため、 JDK10Unsafe
はまだ使用されています。AtomicInteger
使用可能なさまざまなgetメソッドとsetメソッドについて詳しく知りたい場合は、JDK 9メモリ順序モードの使用を参照してください(私はこれについてまったく専門家ではないと言わざるを得ません(まだ?))。
現在のところ、KotlinではgetメソッドとsetメソッドのVarHandle
varargパラメーターをラップしているため、使用できないことに注意してください。バグKT-26165を参照してください。Object...
Object[]
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(VarHandle,byte[],int,byte)void to (VarHandle,Object[])void
(今すぐ修正する必要があります)
これはどう:
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;
セルはコンテンツも揮発性にします。また、セルを事前に割り当てた後でのみ、新しいアレイを揮発性アレイに割り当てます...セルの余分なメモリのトレードオフがありますが、管理可能です