最近、私は次のような文に出会ったという点で、チュートリアルを読んでいました。
「Java言語仕様は、変数の読み取りまたは書き込みがアトミック操作であることを保証します(変数のタイプがlong
またはdouble
でない限り)。タイプlong
またはdouble
は、volatile
キーワードで宣言された場合にのみアトミックです。 "
getAndDecrement()
、getAndIncrement()
、getAndSet()
などのアトミックなメソッドを提供するAtomicInteger
またはAtomicLong
。
上記のステートメントと少し混乱しました。使用する場合AtomicInteger
またはAtomicLong
クラスを明確にしてください。
_a = 28
_(a
がint
である場合)の実行はアトミック操作です。ただし、_a++
_の実行は、aの値の読み取り、増分、および結果への書き込みを必要とするため、アトミック操作ではありません。その結果、_a++
_を使用してスレッドセーフカウンターを実装した場合、2つのスレッドが同時に値を読み取り(たとえば26)、その後で値をインクリメントと書き込みの両方を行うことができ、結果として27になります28の代わりに、結果。
AtomicIntegerは、リストしたようなアトミック操作を提供することにより、この問題を解決します。私の例では、incrementAndGet()
を使用します。これにより、終了値が27ではなく28になることが保証されます。
アトミックとは、間に何かが発生する可能性がない状態で操作が完了することを意味します。例えば。 AtomicIntegerのgetAndDecrement()は、変数が返されると同時にデクリメントされることを保証します。
アトミック操作ではない場合、値が減分され(たとえば3から2)、別のスレッドによって変更され(たとえば2から5に変更)、5として返される可能性があります。
変数を読み取って結果を書き込む必要がある場合は、AtomicInteger
が必要です読み取り値に応じて。例えば、 i++
はi
を読み取ります(例:3
)書き込みi+1
(例:4
)。その間にスレッドが中断される可能性があり、他の3つのスレッドもi
をインクリメントします。戻ると、i
の値は実際には6
しかし、スレッドはまだ4
、事前に読み取った内容に基づきます。
AtomicInteger.getAndIncrement
は、中断されないであるため、常に適切に増加します。さらに、結果は常にメモリにフラッシュですが、不揮発性i
はメモリにフラッシュされない場合があります。この場合、他のスレッドは変更を見ることさえできません。
私はそれが意味することは、長くて二重の読み取り操作がアトミックであり、書き込み操作がアトミックであることだと思います。しかし、読み取りと書き込みはアトミックではありません。
volatile long num;
num = num+1
上記はスレッドセーフではありません。読み取りと書き込みは2つの別個の操作です。これらはそれぞれアトミックであることが保証されていますが、式全体はアトミックではありません。
スレッドセーフにするには、AtomicLongを使用し、getAndIncrement関数を使用する必要があります。
扱う数値の範囲の上限/下限に基づいて、intまたはlongを使用します。 longの非アトミックな動作とAtomicLongを混在させないでください。上に書いたものは何でも正しいですが、おそらく両方の概念を混ぜているでしょう。 AtomicXXXは、「比較と設定」のような操作を行う場合により便利です。たとえば、intをアトミックに変更/読み取りできる場合でも、次のコードはマルチスレッド環境では正しくありません。
int i =10
..
..
..
if(i == 10) i++;
マルチスレッド環境では、2つのスレッドがこのコードにアトミックにアクセスし、iの値を更新して、一貫した状態にすることができます。 SOこのような状況に対処するには、通常、コード「if(i == 10)i ++;」を同期ブロックで保護します。ただし、AtomicIntegerクラスは、同期ブロックを使用せずにAtmoicLong APIの場合も同じです
変数を変更する場合、操作の原子性が必要です。 int a = 10;
を実行することはアトミック操作ですが、問題を引き起こすものではありません。通常、操作を与える問題は、a++
やa = a + 2;
などのように変化するものです。
Java仕様では、「読み取り」と「書き込み」がそれらの組み合わせではなくアトミック操作であることを保証しています。そのため、「1を読み取り、1を追加してから結果を書き戻す」操作は、仕様に従ってアトミックではありません。このような操作は複合操作と呼ばれ、通常、コード内での使用のコンテキストではアトミックである必要があります。
原子型は、この問題の解決に役立ちます。アトミック型でincrementAndget()を使用すると、スレッドセーフのコンテキストで「読み取り、1を追加してから結果を書き戻し、新しい結果を読み取る」単一のアトミック操作が行われます。
お役に立てれば。ちなみに、並行性の基本についての記事( http://walivi.wordpress.com/2013/08/24/concurrency-in-Java-a-beginners-introduction/ )を読んでください。スレッド。そのようなことを美しく説明しています。