複数のスレッドがフィールドにアクセスできる場合、揮発性としてマークする必要がありますか?
質問
いくつかのスレッドを読む(一般的な同時実行の問題、揮発性キーワード、メモリモデル)の同時実行の問題について混乱していますJava。
複数のスレッドからアクセスされるフィールドがたくさんあります。それらを調べて、すべてを揮発性としてマークする必要がありますか?
クラスを構築するとき、複数のスレッドがクラスにアクセスするかどうかはわかりません。したがって、フィールドを 揮発性にしないことは確かに安全ではないため、私の理解では、使用しないでください。これは正しいですか?
これはバージョン1.5のJVM以降に固有ですが、特定のセットアップについての回答に限定されるとは感じません。
解決
フィールドが複数のスレッドによってアクセスされる場合、フィールドは volatile
または final
であるか、同期ブロックでのみアクセスされる必要があります。そうしないと、割り当てられた値が他のスレッドから見えなくなる可能性があります。
クラスは、複数のスレッドによる同時アクセス用に特別に設計する必要があります。単にフィールドをvolatileまたはfinalにマークするだけでは、スレッドセーフには不十分です。一貫性の問題(複数のフィールドへの変更の原子性)、スレッド間シグナルに関する懸念(たとえば、 wait
および notify
の使用)などがあります。
したがって、特に文書化されていない限り、オブジェクトは単一のスレッドのみに表示されると想定するのが最も安全です。すべてのオブジェクトをスレッドセーフにする必要はなく、ソフトウェアの速度という点ではコストがかかりますが、さらに重要なのは開発費用の点です。
代わりに、ソフトウェアは、同時スレッドができるだけ相互作用しないように、好ましくはまったく相互作用しないように設計する必要があります。適切な同時実行制御を設計できるように、それらが相互作用するポイントを明確に識別する必要があります。
他のヒント
さて、あなたはそれらの他の質問を読んだことがあります、そしてあなたはすでに答えを読んだと思いますので、いくつかの重要なポイントを強調します:
- 彼らは変わるでしょうか?そうでない場合、揮発性は必要ありません
- はいの場合、フィールドの値は別のフィールドに関連していますか?はいの場合、ポイント4に進みます
- 変更されるスレッドの数1のみの場合、必要なのはvolatileだけです
- 2番目の答えが「いいえ」の場合または複数のスレッドが書き込みを行う場合、volatileだけでは十分ではありません、おそらくアクセスを同期する必要があります
追加:
フィールドがオブジェクトを参照する場合、フィールドには独自のフィールドがあり、それらの考慮事項はすべてこれらのフィールドにも適用されます。
尋ねる必要がある場合は、ロックを使用します。 volatile
は場合によっては便利ですが、正しく動作させるのは非常に困難です。例:
class Foo {
private volatile int counter = 0;
int Increment() {
counter++;
return counter;
}
}
2つのスレッドが Increment()
を同時に実行すると、結果が counter = 1
になる可能性があります。これは、コンピューターが最初に counter
を取得し、追加してから保存するためです。揮発性は、保存とロードを他のステートメントに関連する特定の順序で強制的に実行するだけです。
synchronized
は通常 volatile
を不要にすることに注意してください-特定のフィールドへのすべてのアクセスが同じモニターによって保護されている場合、 volatile
は必要になることはありません。
volatile
を使用してロックレスアルゴリズムを作成することは、非常に困難です。既に遅すぎるという明確な証拠がなく、実装する予定のアルゴリズムの詳細な分析を行っていない限り、 synchronized
に固執します。
短い答えはノーです。スレッドの問題には、これよりも多くの考えと計画が必要です。 volatileが役立つ場合の制限については、これをご覧ください。スレッド化していない場合。値の変更は適切に同期する必要がありますが、非常に一般的には、一度に複数の変数の状態が必要です。たとえば、変数があり、条件を満たした場合に変更したいとします。アレイからの読み取りとアレイへの書き込みは異なる命令であり、一緒に同期する必要があります。揮発性では不十分です。
変数が可変オブジェクト(配列やコレクションなど)を参照する場合も考慮し、そのオブジェクトと対話することは、参照が揮発性であるという理由だけでスレッドセーフになりません。