これは競合状態ですか?
class A {
int x;
update() {
x = 5;
}
retrieve() {
y = x;
}
}
update()とretrieve()が、ロックを保持せずに2つの異なるスレッドによって呼び出された場合、共有変数の2つのアクセスに少なくとも1つの書き込みがあるとすると、これは競合状態として分類できます。しかし、これは実行時に本当に問題になるのでしょうか。
これは競合状態ですか?
class A {
int x;
update() {
x = 5;
}
retrieve() {
y = x;
}
}
update()とretrieve()が、ロックを保持せずに2つの異なるスレッドによって呼び出された場合、共有変数の2つのアクセスに少なくとも1つの書き込みがあるとすると、これは競合状態として分類できます。しかし、これは実行時に本当に問題になるのでしょうか。
ロックがないと、次の3つのことが起こります。
y
x
(5)の新しい値を取得します。y
x
(ほとんどの場合0)の古い値を取得します。int
がアトミックでない場合y
、他の値を取得できます。Javaでは、への読み取りint
はアトミックであるため、3番目のオプションは発生しません。他の言語の原子性についての保証はありません。
ロックを使用すると、最初の2つのオプションも発生する可能性があります。
ただし、メモリモデルによっては、追加の課題があります。Javaでは、書き込みが同期されていない場合、次の同期ポイント(synchronized
ブロックの終わりまたはvolatile
フィールドへのアクセス)まで任意に遅延させることができます。synchronized
同様に、読み取りは前の同期ポイント(ブロックの開始またはvolatile
フィールドへのアクセス)から任意にキャッシュすることができます。これにより、古いキャッシュに起因する問題が発生しやすくなります。最終的な効果は、最初のオプションが想定されていたとしても、2番目のオプションが発生する可能性があることです。
Javaでは、常にvolatile
他のスレッドからアクセスできるフィールドで使用してください。そうしないと、メモリアクセスの並べ替えによって発生するデバッグが困難な競合状態に直面することになります。同じ警告は、Javaのメモリモデルと同様のメモリモデルを使用する他の言語にも当てはまります。これらの最適化を行わないようにコンパイラに指示する必要がある場合があります。