JAva concurrency in practice読書ノート--javaメモリモデル

2562 ワード

メモリモデルとは?なぜ必要なの?
スレッドが変数aVariableに割り当てられているとします.
avariable=3;

メモリモデルはこの問題を解決する必要があります:どの条件の下で、aVariableを読み取るスレッドはこの値が3になることを見ますか?
この問題はでたらめに聞こえるようですが、マルチスレッドの場合、スレッドがすぐに、さらには別のスレッドの操作結果を永遠に見ることができない多くの要因があります.たとえば、コンパイラで生成される命令の順序は、ソースコードの順序とは異なり、順序を変更することができます.コンパイラはメモリではなくレジスタに変数を保存することができます.プロセッサは乱順または並列などの方法で命令を実行することができる.キャッシュは、変数がプライマリメモリにコミットされる順序を変更する可能性があります.プロセッサのローカルキャッシュに保存されている値は、他のプロセッサでは表示されません.
Java開発者が異なるアーキテクチャ上のメモリモデルの違いに関心を持つ必要がないように、javaは独自のメモリモデルを提供し、JVMは適切な場所にメモリフェンスを挿入することで、JMMと下位プラットフォームのメモリモデルの違いを遮断します.
Javaメモリモデル(JMM)は、変数の読み取り/書き込み操作、モニタのロックと解放操作、スレッドの起動とマージ操作など、さまざまな操作で定義されています.JMMは、Happens−Beforeと呼ばれるプログラム内のすべての動作について、オフセット関係を定義する.
並べ替え
メモリモデルは、プログラムの可能な動作を記述します.特定のコンパイラインプリメンテーションは、任意の好きなコードを生成することができます.これらのコードを実行して生成されたすべての結果が、メモリモデルの予測結果と一致する限り.これは、コンパイラ実装者に大きな自由を提供し、操作の再ソートを含む.
コンパイラが命令を生成する順序は、ソースコードが示す「明らか」バージョンとは異なる場合があります.並べ替えられた命令は,最適化実行および成熟したグローバルレジスタ割り当てアルゴリズムの使用に大きな利点があり,計算性能に大きな向上をもたらした.
並べ替えの種類は次のとおりです.
  • コンパイラが命令を生成する順序は、ソースコードが示す「明らか」バージョン
  • とは異なる.
  • プロセッサは、命令を乱順または並列に実行することができる.
  • キャッシュは、プライマリメモリにコミット変数を書き込む順序
  • を改編する.
    メモリの表示
    現代の共有メモリのマルチプロセッサアーキテクチャでは、1つのスレッドが別のスレッドの操作による結果をすぐに(永遠に)見ることができない可能性があります.したがって、Javaメモリモデルは、JVMの最小保証を規定しています.変数が他のスレッドに表示されるのはいつですか.
    現代の共有メモリのマルチプロセッサアーキテクチャでは、各プロセッサに独自のキャッシュがあり、主メモリと周期的に調整されています.スレッドAが変数値Vを書き込み、その後、別のスレッドBが変数Vの値を読み出すと仮定すると、スレッドBが読み出す値は、スレッドAが書き込む最新値ではない可能性がある.
  • 実行スレッドAのプロセッサは変数Vをレジスタにキャッシュする.
  • スレッドAを実行するプロセッサは変数Vを自分のキャッシュにキャッシュするが,まだ同期してメインメモリにリフレッシュしていない.
  • 実行スレッドBのプロセッサのキャッシュには変数Vの古い値がある.

  • Happens-before関係
    happens-before関係保証:スレッドAとスレッドBがhappens-before関係を満たす場合、スレッドAが動作を実行した結果はスレッドBに対して可視である.2つの操作がhappens-beforeでソートされていない場合、JVMは任意に再ソートできます.
    happens-before関係法則について説明します.
    プログラム順序法則:プログラム内ですべての動作Aが動作Bの前に現れると、スレッド内の各動作Aは、そのスレッド内の各動作Bにhappens−beforeされる.
    モニタロック法則:1つのモニタに対するhappens-beforeのロック解除は、後続の各モニタに対してロックされます.
    Volatile変数法則:Volatileドメインへの書き込み動作happens-beforeは、後続の各Volatileに対する読み取り動作である.
    スレッド起動規則:スレッド上でThread.start()の呼び出しは、スレッド内で任意の操作を実行する前に実行する必要があります.
    スレッド終了規則:スレッド内の任意の操作は、他のスレッドがスレッドが終了したことを検出する前に実行するか、Thread.joinで正常に戻るか、Threadを呼び出します.aliveはfalse を返す
    割り込みルール:あるスレッドが別のスレッドでinterruptを呼び出す場合、割り込みスレッドによってinterrupt呼び出しが検出される前にを実行する必要があります.
    ≪ターミネータ・ルール|Terminator Rules|emdw≫:オブジェクトのコンストラクション関数は、オブジェクトのターミネータを起動する前に完了する必要があります.
    伝達性:A happens-beforeがBにあり、B happens-before Cである場合、A happens-before C.
    参考文献:『Java concurrency in practice』第16章IBM develperWorksドキュメント