、、またはの代わりにvolatile
プリミティブ(たとえば、、boolean
またはinteger
)を使用するのが適切なのはいつですか。その逆も同様です。long
AtomicBoolean
AtomicInteger
AtomicLong
4 に答える
可視性のセマンティクスはまったく同じです。アトミックプリミティブを使用すると便利な状況は、アトミックメソッドを使用する必要がある場合です。
例えば:
if (volatileBoolean) {
volatileBoolean = !volatileBoolean;
}
変数が2行間で変更される可能性があるため、マルチスレッド環境で問題が発生する可能性があります。テストと割り当てをアトミックにする必要がある場合は、次を使用できます。
atomicBoolean.compareAndSet(true, false);
単純なvolatileを使用する方が、値を設定または取得するだけの場合は、より簡単で効率的です。(ただし、値を取得および設定しないでください)
getAndIncrementやcompareAndSwapなどの操作がさらに必要な場合は、AtomicXxxxクラスを使用する必要があります。
クラスは同じタイプのプリミティブをAtomic*
ラップします。volatile
ソースから:
public class AtomicLong extends Number implements java.io.Serializable {
...
private volatile long value;
...
public final long get() {
return value;
}
...
public final void set(long newValue) {
value = newValue;
}
それで。
AtomicBoolean、AtomicInteger、またはAtomicLongの代わりに、揮発性プリミティブ(boolean、integer、longなど)を使用するのが適切な場合
あなたがしているのがaを取得して設定するAtomic*
ことだけであるなら、volatile
代わりにフィールドを持っている方がよいでしょう。
... およびその逆?
ただし、Atomic*
クラスが提供するのは、、などのより高度な機能を提供するメソッドでincrementAndGet()
あり、ロックせずcompareAndSet()
に複数の操作(get / incremental / set、test / set)を実装するメソッドです。そのため、クラスは非常に強力です。Atomic*
volatile
クラスを使用してフィールドをラップするAtomic*
ことは、オブジェクトの観点から重要な共有リソースをカプセル化するための良い方法であることに注意することも重要です。つまり、開発者は、フィールドが共有されていないことを前提として、フィールドを処理することはできませんfield++;
。競合状態を引き起こすコードやその他のコードに問題が発生する可能性があります。
volatile
常に期待どおりに機能するとは限らないコードを書くのは非常に簡単であるため、ソースでの使用を禁止し始めました。
私の経験では、人々はスレッド間で値を共有するために揮発性を追加します。そして、他の誰かが値を変更し始めます。そして、ほとんどの場合、これは機能します。しかし、本番環境では、追跡が非常に難しい奇妙なエラーが発生し始めます。カウンターは100,000回インクリメントされます(テストでは10回だけインクリメントされます)が、最終的には99'997になります。Java 1.4では、長い値が実際に破損する可能性があります。
一方Atomic*
、ヘルパークラスはわずかなオーバーヘッドしか課さず、常に宣伝どおりに機能します。
したがって、使用する非常に正当な理由(*)がない限りvolatile
、常にAtomic*
ヘルパークラスを優先してください。
ヘルパークラスの各キャラクターが何をするのか正確にわからない場合Atomic*
は、本当に避ける必要がありますvolatile
。
*:時期尚早の最適化は決して正当な理由ではありません。