Volatile と Atomic は 2 つの異なる概念です。Volatile は、特定の予期される (メモリ) 状態が異なるスレッド間で真であることを保証しますが、Atomics は、変数に対する操作がアトミックに実行されることを保証します。
Java の 2 つのスレッドの次の例を見てください。
スレッド A:
value = 1;
done = true;
スレッド B:
if (done)
System.out.println(value);
スレッド化のルールから始めてvalue = 0
、done = false
スレッド B が値を出力するかどうかは未定義であることがわかります。さらに、値はその時点でも未定義です! これを説明するには、Java メモリ管理 (複雑になる可能性があります) について少し知っておく必要があります。つまり、スレッドは変数のローカル コピーを作成する場合があり、JVM はコードを並べ替えて最適化することができます。したがって、上記のコードが正確にその順序で実行されます。done を true に設定してからvalueを 1 に設定すると、JIT 最適化の結果が生じる可能性があります。
volatile
そのような変数へのアクセスの瞬間に、新しい値が他のすべてのスレッドにすぐに表示され、実行の順序によって、コードが期待どおりの状態になることが保証されるだけです。したがって、上記のコードの場合、volatiledone
として定義すると、スレッド B が変数をチェックするたびに、変数が false または true のいずれかになり、true の場合は1 にも設定されます。value
volatileの副作用として、このような変数の値はスレッド全体でアトミックに設定されます (実行速度はわずかに低下します)。ただし、これは、長い (64 ビット) 変数 (または同様のもの) を使用する 32 ビット システムでのみ重要です。他のほとんどの場合、変数の設定/読み取りはとにかくアトミックです。しかし、アトミック アクセスとアトミック操作の間には重要な違いがあります。Volatile はアクセスがアトミックであることのみを保証しますが、Atomics は操作がアトミックであることを保証します。
次の例を見てください。
i = i + 1;
i をどのように定義しても、上記の行が実行されたときに値を読み取る別のスレッドが i または i + 1 を取得する可能性があります。これは、操作がアトミックではないためです。他のスレッドが i を別の値に設定した場合、最悪の場合、スレッド A によって以前の値に戻される可能性があります。これは、スレッド A が古い値に基づいて i + 1 を計算している最中だったため、i を設定してから、再びその古い値 + 1 にします。
Assume i = 0
Thread A reads i, calculates i+1, which is 1
Thread B sets i to 1000 and returns
Thread A now sets i to the result of the operation, which is i = 1
AtomicInteger のようなアトミックは、そのような操作がアトミックに行われることを保証します。したがって、上記の問題は発生しません。両方のスレッドが終了すると、i は 1000 または 1001 になります。