回答:
短い答えは次のとおりです。いいえ。
java.util.concurrent.atomic
パッケージのドキュメントから。引用するには:
アトミックのアクセスと更新のメモリ効果は、一般に揮発性物質の規則に従います。
get
volatile
変数を読み取るメモリ効果があります。set
volatile
変数の書き込み(割り当て)のメモリー効果があります。
ちなみに、そのドキュメントは非常に優れており、すべてが説明されています。
AtomicReference::lazySet
volatile
変数を介して達成できないセマンティクスを持つ、導入された新しい(Java 6+)操作です。詳細については、この投稿を参照してください。
いくつかの違いとトレードオフがあります。
AtomicReference
get / setの使用は、(javadocが示すように)volatileフィールドと同じJMMセマンティクスを持ってAtomicReference
いますが、これは参照のラッパーであるため、フィールドへのアクセスには、さらにポインターの追跡が必要です。
メモリフットプリントが乗算され(ほとんどのVMについても同様である圧縮OOPS環境を想定します):
AtomicReference
= 4b + 16b(12bオブジェクトヘッダー+ 4b参照フィールド)AtomicReference
揮発性リファレンスよりも豊富なAPIを提供します。を使用するAtomicFieldUpdater
か、Java 9 a を使用すると、揮発性参照のAPIを取り戻すことができますVarHandle
。sun.misc.Unsafe
ハサミで走るのが好きなら、まっすぐ伸ばすこともできます。AtomicReference
自体はを使用して実装されUnsafe
ます。
それで、どちらを選ぶのが良いのですか?
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
あなたはパフォーマンスの向上のための読みやすさとリスクに賃金する傾向があります。これがデリケートな領域でない場合は、そのままにしてくださいAtomicReference
。ライブラリの作成者は通常、対象のJDK、予想されるAPIの制限、メモリの制約などに応じて、これらの方法を組み合わせて使用します。JDKソースコードは、このような混乱に答える最良の方法の1つです。AtomicReferenceのコードを見ると、オブジェクトストレージにボラティ変数が使用されています。
private volatile V value;
したがって、明らかにAtomicReferenceでget()とset()を使用する場合は、揮発性変数を使用するようなものです。しかし、他の読者がコメントしたように、AtomicReferenceは追加のCASセマンティクスを提供します。そのため、最初にCASセマンティクスが必要かどうかを決定し、必要な場合のみAtomicReferenceを使用します。
AtomicReference
プレーンな揮発性変数が提供しない追加機能を提供します。API Javadocを読んでいると、これがわかるでしょうが、一部の操作に役立つロックも提供されます。
ただし、この追加機能が必要でない限り、プレーンvolatile
フィールドを使用することをお勧めします。
volatile
フィールドは通常のフィールドと同じように使用できますが、の値にアクセスするには、AtomicReference
通過するメソッドget
とset
メソッドが必要です。
getとsetのみを使用する場合でも、AtomicReferenceが適切な選択になる場合があります。
揮発性の例:
private volatile Status status;
...
public setNewStatus(Status newStatus){
status = newStatus;
}
public void doSomethingConditionally() {
if(status.isOk()){
System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
}
}
AtomicReferenceを使用した実装では、コピーオンライト同期を無料で提供します。
private AtomicReference<Status> statusWrapper;
...
public void doSomethingConditionally() {
Status status = statusWrapper.get();
if(status.isOk()){
System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
}
}
置き換えた場合でも、適切なコピーを持つことができると言うかもしれません:
Status status = statusWrapper.get();
と:
Status statusCopy = status;
ただし、2つ目は、将来、「コードクリーニング」中に誤って誰かによって削除される可能性が高くなります。