効果的なJavaの本では、それは述べています:
言語仕様は、変数の型が
long
またはdouble
でない限り、変数の読み書きはアトミックであることを保証します[JLS、17.4.7]。
「アトミック」とは、Javaプログラミング、またはプログラミング一般の文脈ではどういう意味ですか?
例は長い説明よりも明確であることが多いため、ここに例があります。 foo
がlong
型の変数であるとします。次の操作はアトミック操作ではありません。
foo = 65465498L;
実際、変数は最初の32ビットを書き込む操作と最後の32ビットを書き込む操作という2つの別々の操作を使用して書き込まれます。これは、別のスレッドがfoo
の値を読み取り、中間状態を見る可能性があることを意味します。
操作をアトミックにすることは、その操作が他のどのスレッドからも単一のアトミック(すなわち、部分的に分割できない)操作として見られるようにするために同期メカニズムを使用することからなる。つまり、操作がアトミックになると、他のスレッドは代入前または代入後にfoo
の値を参照します。しかし、決して中間的な価値はありません。
これを行う簡単な方法は、 変数volatile を作ることです。
private volatile long foo;
または、変数へのすべてのアクセスを同期するには
public synchronized void setFoo(long value) {
this.foo = value;
}
public synchronized long getFoo() {
return this.foo;
}
// no other use of foo outside of these two methods, unless also synchronized
あるいは AtomicLong
に置き換えます。
private AtomicLong foo;
「アトミック操作」とは、他のすべてのスレッドから見て瞬間的に見える操作を意味します。保証が適用されるとき、あなたは部分的に完全な操作について心配する必要はありません。
それは「システムの残りの部分に瞬時に発生するように見える」ものであり、コンピューティングプロセスでは 線形化可能性 の分類に分類されます。リンクされた記事をさらに引用するには:
アトミック性は、並行プロセスからの分離の保証です。さらに、アトミック操作には通常、成功または失敗の定義があります。システムの状態を正常に変更するか、明らかな効果はありません。
そのため、たとえば、データベースシステムのコンテキストでは、「アトミックコミット」を行うことができます。つまり、更新の変更セットをリレーショナルデータベースにプッシュすると、それらの変更はすべて送信されるか、このように、データが破損せず、ロックやキューの結果となる障害のイベント、次の操作は異なる書き込みまたは読み取りになりますが、after事実。変数とスレッドのコンテキストでは、これはほとんど同じであり、メモリに適用されます。
あなたの引用は、これがすべてのインスタンスで期待される振る舞いではないことを強調しています。
ちょうど私にとって非常に有用であるポスト Atomic vs. Non-Atomic Operations を見つけました。
msgstr "共有メモリに作用する操作は他のスレッドに対して単一のステップで完了するならばアトミックです。
アトミックストアが共有メモリ上で実行されると、他のスレッドは変更が半分完了したことを確認できません。
共有変数に対してアトミックロードが実行されると、単一の瞬間に現れた値全体が読み取られます。」
以下のコードでメソッドm1とm2を実行するスレッドがいくつかあるとします。
class SomeClass {
private int i = 0;
public void m1() { i = 5; }
public int m2() { return i; }
}
m2
を呼び出すすべてのスレッドが0または5のいずれかを読み取ることを保証します。
一方、このコードでは(i
はlongです):
class SomeClass {
private long i = 0;
public void m1() { i = 1234567890L; }
public long m2() { return i; }
}
m2
がlong
のアトミックであることは保証されていないため、i = 1234567890L
を呼び出すスレッドは0、1234567890L、またはその他のランダムな値を読み取ることができます(JVMは2つの操作で最初の32ビットと最後の32ビットを書き込むことができます)。間のi
)。