37

volatileJavaで配列要素を宣言する方法はありますか?つまり

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

配列参照 を宣言しますvolatileが、配列要素(たとえばa[1])はまだ揮発性ではありません。だから私は次のようなものを探しています

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

しかし、それはそのようには機能しません。それは可能ですか?

4

4 に答える 4

32

AtomicIntegerArrayまたはを使用AtomicLongArrayするAtomicReferenceArray

このクラスは、クラスとメソッドAtomicIntegerArrayを介して、揮発性セマンティクスで個々のフィールドにアクセスできるint配列を実装します。あるスレッドから呼び出すと、別のスレッド呼び出しが値yを読み取ることが保証されます(別の値が位置xに読み取られるまで)。get()set()arr.set(x, y)arr.get(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

これを行う別の方法は、JDK9+VarHandleクラスを使用することです。AtomicのようなxxxArrayクラスのソースコードでわかるようAtomicIntegerArrayに、これらのクラスはVarHandleJDK9以降でも使用されます。

//[...]

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)メソッドとgetxxx(array, index)メソッドを使用してアクセスできます。ここで、arrayはタイプyourArrayClassindexはタイプintvalueは配列内の要素のタイプです(yourArrayClass.getComponentType())。

たとえば、としてyourArrayClass == byte[].class入力42した場合、はaの代わりにであり、アクセスメソッドのパラメータはvarargパラメータであるvalueため、エラーが発生することに注意してください。42intbyteObject...

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メソッドのVarHandlevarargパラメーターをラップしているため、使用できないことに注意してください。バグKT-26165を参照してください。Object...Object[]

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;

セルはコンテンツも揮発性にします。また、セルを事前に割り当てた後でのみ、新しいアレイを揮発性アレイに割り当てます...セルの余分なメモリのトレードオフがありますが、管理可能です

于 2019-03-06T10:13:56.083 に答える