ホームページ  >  記事  >  Java  >  Java 同時プログラミングの揮発性および JMM マルチスレッド メモリ モデルの分析例

Java 同時プログラミングの揮発性および JMM マルチスレッド メモリ モデルの分析例

王林
王林転載
2023-05-27 08:58:481221ブラウズ

1. プログラムで現象を確認する

Java マルチスレッド キャッシュ モデルの説明を始める前に、まず次のコードを見てみましょう。このコードのロジックは非常に単純です。メインスレッドは 2 つのサブスレッド (スレッド 1 とスレッド 2) を開始します。スレッド 1 が最初に実行され、スレッド 2 は 2 秒間スリープした後に実行されます。 2 つのスレッドは、初期値が false の共有変数 shareFlag を使用します。 shareFlag が常に false の場合、スレッド 1 は常に無限ループになるため、スレッド 2 で shareFlag を true に設定します。

public class VolatileTest {
  public static boolean shareFlag = false;
  public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
      System.out.print("开始执行线程1 =>");
      while (!shareFlag){  //shareFlag = false则一直死循环
        //System.out.println("shareFlag=" + shareFlag);
      }
      System.out.print("线程1执行完成 =>");
    }).start();
    Thread.sleep(2000);
    new Thread(() -> {
      System.out.print("开始执行线程2 =>");
      shareFlag = true;
      System.out.print("线程2执行完成 =>");
    }).start();
  }
}

JMM スレッド モデルをまだ学習していない場合は、上記のコードを読んだ後、出力結果が次のようになることを期待しているでしょう。

スレッド 1 の実行を開始 =>スレッド 2 の実行を開始 => スレッド 2 の実行が完了 => スレッド 1 の実行が完了 =>

このコードは下図のように、普通の人なら理解できるコードなので、まずスレッド 1 を実行してループに入ります。 2 shareFlag=true を変更すると、スレッド 1 がループから抜け出します。したがって、ループから抜け出したスレッド 1 は「スレッド 1 の実行が完了しました =>」と出力されますが、筆者の実験後、**「スレッド 1 の実行が完了しました =>」は出力されず、スレッド 1 はループから抜け出すことはありません。無限ループ**。これはなぜですか?

Java 同時プログラミングの揮発性および JMM マルチスレッド メモリ モデルの分析例

2. この現象はなぜ発生しますか (JMM モデル)。

上記の問題を説明するには、JMM (Java Memory Model) Java メモリ モデルを学習する必要がありますが、Java マルチスレッド メモリ モデルと呼ぶ方が正確だと思います。

Java 同時プログラミングの揮発性および JMM マルチスレッド メモリ モデルの分析例

  • まず、JMM では、各スレッドが独自の作業メモリを持ち、プログラムが開始されると、スレッドは共有変数をロードします (read&load)。独自の作業メモリ内では、スレッドの作業メモリにロードされるメモリ変数は、メイン メモリ内の共有変数のコピーです。つまり、現時点ではメモリ内に shareFlag のコピーが 3 つあり、それらの値はすべて false に等しいということです。

  • スレッド 2 が shareFlag=true を実行すると、その作業メモリのコピーが shareFlag=true に変更され、コピーの値は次のようになります。同時に同期され、メインメモリにライトバック(ストア&ライト)されます。

  • しかし、スレッド 1 の作業メモリ内の shareFlag=false は変更されていないため、スレッド 1 は無限状態になります。ループ。中央

3. MESI キャッシュ コヒーレンス プロトコル

スレッド 2 による共有変数の変更は、スレッド 1 には認識されません。これは、上記の実験結果と JMM と一致します。モデル。では、スレッド 1 は共有変数の値が変更されたことをどのように認識できるのでしょうか?実際、これは非常に簡単で、shareFlag 共有変数に volatile キーワードを追加するだけです。

public volatile static boolean shareFlag = false;

基本的な原理は次のとおりです。volatile キーワードを追加すると、JMM は MESI キャッシュ整合性プロトコルに従うように求められます。このプロトコルには、次のキャッシュ使用仕様が含まれています (理解できない場合は、無視してください。以下で使用します。簡単な言葉と例で説明してください)。

  1. Modified: 現在のキャッシュ ラインのデータが変更 (ダーティ) されており、現時点では現在の CPU のキャッシュ内でのみ変更されていることを表します。 、キャッシュ行のデータは、他のキャッシュのデータやメモリ内の行のデータとは異なります。

  2. Exclusive: 現在のキャッシュ ラインのデータが有効なデータであり、他の CPU のキャッシュにはそのようなデータ ラインが存在しないことを示します。現在のキャッシュラインのデータはメモリ内のデータと異なります。

  3. Shared: このデータ行は複数の CPU を表すキャッシュにキャッシュされ、キャッシュ内のデータはメモリ内のデータと一致します。

  4. Invalid: 現在のキャッシュ行のデータが無効であることを示します;

Java 同時プログラミングの揮発性および JMM マルチスレッド メモリ モデルの分析例

上記のキャッシュ 使い方の仕様が複雑すぎるかもしれませんが、簡単に言うと、

  • スレッド 2 が shareFlag を変更したとき (「変更」を参照)、共有フラグを変更したことをバスに通知します。変数 shareFlag,

  • スレッド 1 はバスを監視し、共有変数 shareFlag が変更されたことを知ると、作業メモリ内の shareFlag のコピーを削除して無効にします。

  • #スレッド 1 が shareFlag を再度使用する必要があり、作業メモリに shareFlag 変数のコピーがないことが判明すると、メイン メモリからリロード (読み取り&ロード) されます

以上がJava 同時プログラミングの揮発性および JMM マルチスレッド メモリ モデルの分析例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。