public class Demo09 { public static boolean flag = true; public static class T1 extends Thread { public T1(String name) { super(name); } @Override public void run() { System.out.println("线程" + this.getName() + " in"); while (flag) { ; } System.out.println("线程" + this.getName() + "停止了"); } } public static void main(String[] args) throws InterruptedException { new T1("t1").start(); //休眠1秒 Thread.sleep(1000); //将flag置为false flag = false; } }
上記のコードを実行すると、プログラムを終了できないことがわかります。
スレッド t1 の run() メソッドにループがあります。ループが終了するかどうかを制御するためにフラグが使用されます。メインスレッドは 1 秒間スリープし、フラグを false に設定します。今回、スレッド t1 はフラグが false であることを検出し、「スレッド t1 が停止しました」と出力します。結果が期待したものと異なるのはなぜですか?上記のコードを実行すると、t1 に見られるフラグは常に true であると判断できますが、メインスレッドがフラグを false に設定した後は、t1 スレッドには見られないため、ループし続けます。
それでは、t1 のメインスレッドによって変更されたフラグが表示されないのはなぜでしょうか?
これを説明するには、まず Java メモリ モデル (JMM) について理解する必要があります。Java スレッド間の通信は、Java メモリ モデル (この記事では JMM と呼びます) によって制御されます。JMM は、スレッドの書き込み方法を決定します。共有変数へ。別のスレッドから見える場合。抽象的な観点から、JMM はスレッドとメイン メモリの間の抽象的な関係を定義します。スレッド間の共有変数はメイン メモリ (メイン メモリ) に格納され、各スレッドは共有メモリのコピーであるプライベート ローカル メモリ (ローカル メモリ) を持ちます。スレッドが読み書きする変数はローカル メモリに保存されます。ローカル メモリは JMM の抽象概念であり、実際には存在しません。キャッシュ、書き込みバッファ、レジスタ、その他のハードウェアとコンパイラの最適化について説明します。 Java メモリ モデルの抽象的な概略図は次のとおりです。
上の図からわかるように、スレッド A はスレッド B と通信する必要があり、スレッド B を通過する必要があります。次の 2 つの手順:
1. まず、スレッド A がローカル メモリ A 内の更新された共有変数をメイン メモリ
2 に更新します。次に、スレッド B がメイン メモリに移動して以前の変数を読み取ります。スレッド A の共有変数を更新しました。 共有変数
次は、これら 2 つのステップを示す概略図です。
上の図に示すように、ローカルメモリ A と B は、x のメイン メモリ コピーに共有変数を持っています。最初、これら 3 つのメモリの x 値がすべて 0 であると仮定します。スレッド A の実行中、スレッド A は更新された x 値 (値が 1 であると仮定) を独自のローカル メモリ A に一時的に保存します。スレッド A とスレッド B が通信する必要がある場合、スレッド A はまずローカル メモリ内の変更された x 値をメイン メモリにリフレッシュします。このとき、メイン メモリ内の x 値は 1 になります。続いて、スレッド B はメイン メモリに行き、スレッド A の更新された x 値を読み取ります。このとき、スレッド B のローカル メモリの x 値も 1 になります。全体として、これら 2 つのステップは基本的に、スレッド A がスレッド B にメッセージを送信することであり、この通信プロセスはメイン メモリを経由する必要があります。 JMM は、メイン メモリと各スレッドのローカル メモリ間の相互作用を制御することにより、Java プログラマにメモリの可視性を保証します。
JMM を理解した後、記事の冒頭の質問を見てみましょう。スレッド t1 のメインスレッドによって false に変更されたフラグの値が表示されないのはなぜですか? 考えられる可能性は 2 つあります。 :
1 .メイン スレッドがフラグを変更した後、フラグをメイン メモリに更新しなかったため、t1 は
2 を認識できませんでした。メイン スレッドはフラグをメイン メモリに更新しました, しかし、t1 は常に独自の作業メモリ内のフラグを読み取ります。値は、フラグの最新の値を取得するためにメイン メモリに移動しません。
上記の 2 つの状況について、解決する方法はありますか?それ?
そのような方法はありますか: スレッドが作業メモリ内のコピーを変更した後、共有変数が作業メモリに読み取られるたびに、すぐにメインメモリに更新されます。メインメモリから再度読み取り、作業メモリにコピーします。
Java はそのようなメソッドを提供します。volatile を使用して共有変数を変更すると、上記の効果を実現できます。volatile によって変更された変数には次の特性があります:
1. スレッド内で読み取りのたびに、メイン メモリから共有変数の最新の値が読み取られ、それが作業メモリ
2にコピーされます。スレッドは、作業メモリ内の変数のコピーを変更します。変更後、 , これはすぐにメイン メモリにフラッシュされます。
最初のサンプル コードを変更しましょう:
public volatile static boolean flag = true;
volatile を使用してフラグ変数を変更し、プログラムと出力:
线程t1 in 线程t1停止了
これで、プログラムを正常に停止できます。 Volatile は、マルチスレッドでの共有変数の可視性の問題を解決します。可視性とは、あるスレッドによる共有変数の変更が別のスレッドに見えるかどうかを指します。 \
以上がJava の高同時実行性の揮発性メモリ モデルと Java メモリ モデルは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。