私はまだスレッドの概念にかなり慣れていないので、スレッドについてもっと理解しようとしています。最近、ジェレミー・マンソンによる Javaでの揮発性の意味 に関するブログ投稿を見つけました。
1つのスレッドが揮発性変数に書き込み、別のスレッドがその書き込みを確認すると、最初のスレッドは2番目のスレッドにメモリの内容のallを伝えますその揮発性変数への書き込みを実行しました。 [...]allは、スレッド1が
[volatile] ready
に書き込む前に、メモリ2に表示される内容を、スレッド2に表示する必要があります。true
の値ready
を読み取った後。 [自分で追加した強調]
さて、それは、揮発性変数への書き込み時にスレッド1のメモリに保持されているすべての変数(揮発性かどうか)が、その揮発性変数を読み取った後、スレッド2から見えるようになるということですか?もしそうなら、公式のJava documentation/Oracleソースからそのステートメントを一緒に謎にすることは可能ですか?そして、どのバージョンのJava以降は動作しますか?
特に、すべてのスレッドが次のクラス変数を共有する場合:
private String s = "running";
private volatile boolean b = false;
そして、スレッド1は最初に以下を実行します。
s = "done";
b = true;
その後、スレッド2が実行されます(スレッド1がvolatileフィールドに書き込んだ後)。
boolean flag = b; //read from volatile
System.out.println(s);
これは「完了」を印刷することが保証されますか?
b
をvolatile
として宣言する代わりに、書き込みと読み取りをsynchronized
に入れたらどうなりますかブロック?
また、「 静的変数はスレッド間で共有されますか? 」というタイトルのディスカッションでは、@ TREE writes :
Volatileを使用して複数の共有状態を保護しないでください。
理由(申し訳ありません。他の質問にはまだコメントできません。そこ...)
はい、スレッド2が "done"を出力することが保証されています。もちろん、スレッド1のb
への書き込みが、スレッド2のb
からの読み取りの前に、同時またはそれ以前に発生するのではなく、実際に発生する場合です。
ここでの推論の中心は happens-before relationship です。マルチスレッドプログラムの実行は、イベントで構成されていると見なされます。イベントは、発生前の関係によって関連付けることができます。つまり、あるイベントが別のイベントの前に発生します。 2つのイベントが直接関連していなくても、あるイベントから別のイベントへの前に発生する関係のチェーンを追跡できれば、一方が他方の前に発生すると言えます。
あなたの場合、次のイベントがあります:
s
に書き込みますb
に書き込みますb
から読み取りますs
から読み取りますそして、以下のルールが作用します:
したがって、次の発生前の関係が存在します。
s
に書き込む前に発生するスレッド1がb
に書き込む(プログラム順序ルール) )b
に書き込む前に発生するスレッド2がb
から読み取る(揮発性ルール)b
から読み取る前に発生するスレッド2がs
から読み取る(プログラム順序ルール) )そのチェーンをたどると、結果として次のことがわかります。
s
に書き込む前にスレッド2がs
から読み取るBをvolatileとして宣言する代わりに、書き込みと読み取りを同期ブロックに配置するとどうなりますか?
If and only ifこのような同期されたすべてのブロックを同じロックで保護すると、volatile
の例と同じように可視性が保証されます。さらに、そのような同期されたブロックの実行を相互に排除します。
Volatileを使用して複数の共有状態を保護しないでください。
どうして?
volatile
は原子性を保証しません。この例では、表示している書き込みの後に、s
変数が他のスレッドによって変更された可能性があります。読み取りスレッドには、どの値が表示されるかについての保証はありません。 s
の読み取り後、ただしvolatile
の読み取り前に発生するs
への書き込みについても同様です。
安全で実際に行われることは、不変状態の共有volatile
変数に書き込まれた参照から推移的にアクセスできることです。つまり、それが「共有状態の1つの部分」が意図する意味かもしれません。
公式のJavaドキュメント/ Oracleソースから)そのステートメントを一緒に困惑させることは可能ですか?
仕様からの引用:
17.4.4。同期の順序
揮発性変数v(8.3.1.4)への書き込みは、任意のスレッドによるvの後続のすべての読み取りと同期します(「後続」は同期順序に従って定義されます)。
17.4.5。注文前に発生
Xとyが同じスレッドのアクションであり、xがプログラム順にyの前にある場合、hb(x、y)です。
アクションxが次のアクションyと同期する場合、hb(x、y)も得られます。
これで十分でしょう。
そして、Java以降のどのバージョンから、これは動作しますか?
Java言語仕様、第3版では、上記の保証の鍵となるメモリモデル仕様の書き換えが導入されました。 NB以前のほとんどのバージョンは、保証が存在するかのように動作し、多くのコード行が実際にそれに依存していました。保証が実際に存在しないことを知ったとき、人々は驚きました。
これは「完了」を印刷することが保証されますか?
Java Concurrency in Practice で述べたように:
スレッド
A
がvolatile
変数に書き込み、その後スレッドB
が同じ変数を読み取る場合、に表示されていたすべての変数の値volatile
変数への書き込み前のAは、volatile
変数を読み取った後、Bに表示されます。
したがって、[〜#〜]はい[〜#〜]、これにより、「完了」の印刷が保証されます。
Bをvolatileとして宣言する代わりに、書き込みと読み取りを同期ブロックに配置するとどうなりますか?
これも同じことを保証します。
Volatileを使用して複数の共有状態を保護しないでください。
どうして?
なぜなら、volatileは可視性のみを保証します。原子性は保証されません。スレッドA
によってアクセスされているメソッドに2つの揮発性書き込みがあり、別のスレッドB
がそれらの揮発性変数にアクセスしている場合、スレッドA
がメソッドを実行している間スレッドA
は、操作の途中でスレッドB
によってプリエンプトされる可能性があります(たとえば、最初の揮発性書き込みの後、スレッドA
による2番目の揮発性書き込みの前)。したがって、操作の原子性を保証することは、synchronization
が最も実現可能な方法です。