web-dev-qa-db-ja.com

Java 9でVarHandleを使用する正しい方法?

私はJava 9'sの新機能のいくつかを調査することに多くの時間を費やしてきましたが、有用で実用的な例は見つかりませんでした。

VarHandleを作成する次のコードスニペットを検討してください。

class Counter {
    int i;
}

class VarHandleInAction {
    static final VarHandle VH_COUNTER_FIELD_I;

    static {
        try {
            VH_COUNTER_FIELD_I = MethodHandles.lookup()
                .in(Counter.class)
                .findVarHandle(Counter.class, "i", int.class);
        } catch (Exception e) {
            // ...
        }
    }
}

しかし、次は何ですか?つまり、これを使用する方法変数ハンドル?実際の例を提供できますか?

30

これは、AtomicReferenceで使用されています。以前はJava 8、で使用されていましたが、Sun.misc.Unsafeが使用されていました:

public final void lazySet(V newValue) {
    unsafe.putOrderedObject(this, valueOffset, newValue);
}

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

ここでは、thisポインターをフィールドオフセットと共に使用して、フィールドにアクセスします。しかし、このフィールドオフセットはlongになる可能性があり、実際にはまったく異なるものにアクセスしている可能性があるため、これは安全ではありません。ただし、このようにするとパフォーマンスが向上し(VMに特殊なCPU命令を使用するように指示するなど)、そのため、他の人がSun.misc.Unsafeを使用している内部、およびunsafeAPI。

VarHandlesの目的の一部は、Sun.misc.Unsafeの操作を安全な同等のものに置き換えることです。 JEP に記載されています:

さまざまなJava.util.concurrent.atomicおよびSun.misc.Unsafe操作に相当するものを呼び出すための標準的な手段を定義します...

目標:

以下は必須の目標です。

  • 安全性。 Java仮想マシンを破損したメモリ状態にすることはできません。たとえば、オブジェクトのフィールドは、フィールドタイプにキャスト可能なインスタンスでのみ更新できます。配列要素が配列内にアクセスできるのは、配列インデックスが配列境界内にある場合のみです。

  • 誠実さ。オブジェクトのフィールドへのアクセスは、オブジェクトの最終フィールドを更新できないという制約に加えて、getfieldおよびputfieldバイトコードと同じアクセスルールに従います。 (注:このような安全性と整合性のルールは、フィールドへの読み取りまたは書き込みアクセス権を与えるMethodHandlesにも適用されます。)

  • パフォーマンス。パフォーマンス特性は、同等のSun.misc.Unsafe操作と同じまたは類似している必要があります(具体的には、生成されたアセンブラコードは、特定の安全性チェックを折りたたむことができないため、ほぼ同一でなければなりません)。

  • 使いやすさ。 APIは、Sun.misc.Unsafe APIよりも優れている必要があります。

したがって、Java 9では、これらのメソッドは次のようになります。

public final void lazySet(V newValue) {
    VALUE.setRelease(this, newValue);
}

public final boolean compareAndSet(V expectedValue, V newValue) {
    return VALUE.compareAndSet(this, expectedValue, newValue);
}

ここで、VALUEVarHandleのように定義します。

private static final VarHandle VALUE;
static {
    try {
        MethodHandles.Lookup l = MethodHandles.lookup();
        VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class);
    } catch (ReflectiveOperationException e) {
        throw new Error(e);
    }
}
31
Jorn Vernee