Javaメモリモデルの簡単な紹介


詳細
Javaメモリモデルといえば最初からJava仮想マシン内のデータ領域の概念を思い浮かべるかもしれませんが、ここのJavaメモリモデル(JMM,Java memory model)とは、Java言語仕様で定義されたメモリモデルで、オンラインスレッド間データの可視性上の定義であり、Javaではデータが見られない場合があります.つまり、プログラムの上で欲しい結果が得られるように見えますが、現実的には実行された結果は自分が望んでいるものではありません.次の例です(直接叩くコードは正しくないかもしれませんが、多分そうです)
 
public class TestSee{
     boolean canSee = true;

     public static void main(String [] args){
           TestSee testSee = new TestSee();
           new Thread(() -> {
               int i = 0;
               while(testSee.canSee){
                    i++;
               }
               System.out.println(i);
          }).start();

         Thread.sleep(3000);
         testSee.canSee = false;
     }
}

これはサブスレッドを起動することによってcanSeeがtrueであると判断したときに実行され、メインスレッド全体が3秒休止した後、canSeeをfalseに付与し、falseに変更した後にサブスレッドもプログラムの実行を終了する効果が期待されるが、異なるパラメータの場合にサブスレッドは終了できない可能性がある.canSeeがfalseに変更されたことを知らない可能性があるため、キャッシュによる可能性がありますが、なぜキャッシュなのでしょうか.これはJavaの解釈実行とコンパイル実行を理解する必要があります.Javaが一定時間の解釈実行を実行するとプログラムに変化がないため、メモリリソースをよりよく節約するために、JIT(just in time complier)の方式でコンパイル実行を命令再配置することで、コードをキャッシュします.次のような偽コードも現れます
boolean see = canSee;
while(true){
   i++;
}

だから後でcanSeeが変わったかどうかにかかわらず、正しい応答が得られない可能性があります.
では、キャッシュを防止するにはどのような方法がありますか?答えはvolatileキーワードを使用することです.このキーワードを使用すると仮想マシンは自動的にキャッシュと命令の並べ替えを無効にします.volatileキーワードで修飾された変数を変更すると、短い時間でJavaで変数の作業スレッドとメインスレッドの修正値を伝達することで、タイムリーな応答値の変化が得られます.これにより、volatileキーワードで修飾された変数には、スレッド間のデータの可視性が達成されます.
      1.変数の変更は、後のすべての他のスレッドに表示されます.
      2.変数の修正A動作が動作Bの前に発生し、動作Bが動作Cの前に発生すると、変数の修正A動作もC動作に可視性を生じる
      3.スレッドt 1がスレッドt 2を正常に実行する場合.join()メソッドでは、t 1のすべての動作がt 2に表示される
上記の特徴はhappen-before原則とも呼ばれるので、共通変数の場合、頻繁に修正し、他のスレッドが参照する必要がある場合はvolatileキーワードを使用して修飾することが望ましい.
データの可視性同期保証にはvolatileキーワードのほかに、次のような状況があります.
      1.ロック/ロック解除を使用して制御すると、命令の再配置は発生しません.
       2.スレッド起動時にそのスレッドの最初に実行されるコマンドと同期して保証される
       3.サブスレッドの最後の操作は、プライマリスレッドと同期して保証されます.
       4.メインスレッドでinterrupt方式でサブスレッドの実行が中断された場合、この中断操作は他のサブスレッドにも表示されます.もちろん、ここでは必ずしもメインスレッドが中断操作を実行するわけではありません.他のサブスレッドのみが中断サブスレッドのインスタンスを使用して中断操作を実行できます.
使用中に不変の変数に対応するにはfinalを使用して修飾することが望ましく、すべてのスレッドの可視性と変更不可を保証すると同時に、共有byte[]配列を変更しないことが望ましい.これはデータ整合性の問題をもたらし、volatileキーワードは64ビットの共有値(double,long)に対してもより良い.