このような単一のアトミック操作を持つメソッドがあります
int value;
public void setValue(int value) {
this.value = value;
}
次に、次のように明白な方法で呼び出します
foo.setValue(10);
問題は、それはアトミック操作でしょうか? いいえの場合、どのアトミック操作が実行されますか? 自分のコンピューターでこれをテストするにはどうすればよいですか (できる場合)。
このような単一のアトミック操作を持つメソッドがあります
int value;
public void setValue(int value) {
this.value = value;
}
次に、次のように明白な方法で呼び出します
foo.setValue(10);
問題は、それはアトミック操作でしょうか? いいえの場合、どのアトミック操作が実行されますか? 自分のコンピューターでこれをテストするにはどうすればよいですか (できる場合)。
はい
this.value = value;
操作はアトミックです。Java 言語仕様: スレッドとロックを参照してください。
ただし、スレッドは不揮発性変数の独自の値をキャッシュすることが許可されているため、連続する get操作で最後のset valueが得られるとは限りません。
この種のデータ競合を取り除くには、何らかの方法で変数へのアクセスを同期する必要があります。これは、
AtomicInteger
から使用します。java.util.concurrent
(好ましい方法です)からまたはに変更した場合、操作はアトミックではないことにも注意してください。JLS の関連セクションを次に示します。int
long
double
17.4 double と long の非アトミック処理
double または long 変数が volatile であると宣言されていない場合、ロード、ストア、読み取り、および書き込みアクションの目的で、それらはそれぞれ 32 ビットの 2 つの変数であるかのように扱われます。アクションは、32 ビットの半分ごとに 1 つずつ実行されます。
いくつかの便利なリンク:
これは原始的な 32 ビット値であるため、アトミックです。
したがって、それを読むと、いずれかのスレッドによって設定された値が表示されることが保証されますが、それがどのスレッドであったかはわかりません。
実際には、ほとんどの VM 実装は書き込みをアトミック操作として扱いますが、それが の場合long
、同じ保証はありません。long
この問題について JLS は次のように述べています。
VM の実装者は、可能な限り 64 ビット値を分割しないようにすることをお勧めします。プログラマーは、共有 64 ビット値を volatile として宣言するか、プログラムを正しく同期して複雑化を回避することをお勧めします。
しかし、int を使用すると安全です。問題は、この非常に弱い保証で十分ですか? 多くの場合、答えはノーです。
まず、Java のすべてのプリミティブ型 (64 ビットのものを除く) への割り当ては、Java 仕様に従ってアトミックです。ただし、たとえば、使用するタイプに関係なく、自動インクリメントはスレッドセーフではありません。
しかし、このコードの本当の問題は原子性ではなく可視性です。2 つのスレッドが を変更している場合value
、互いの変更が認識されないことがあります。キーワードを使用するvolatile
か、さらに良い方法として、AtomicInteger
正しい同期と可視性を保証します。
synchronized
キーワードは可視性も保証することに注意してください。つまりsynchronnized
、ブロック内で何らかの変更が発生した場合、他のスレッドから見えることが保証されます。