Java スレッドの同時処理では、現在、キーワード volatile の使用について多くの混乱が生じています。このキーワードを使用すれば、マルチスレッドの同時処理を実行する場合は問題ないと考えられています。
Java 言語はマルチスレッドをサポートしており、スレッドの同時実行性の問題を解決するために、同期ブロックと volatile キーワードのメカニズムが言語内に導入されています。
同期した
synchronized キーワードによって実装される synchronized ブロックについては誰もがよく知っていますが、synchronized ステートメントと block ステートメントを追加すると、複数のスレッドからアクセスされる場合、同時に 1 つのスレッドだけが同期された変更されたメソッドまたはコード ブロックを使用できます。
揮発性の
volatile で変更された変数の場合、スレッドが変数を使用するたびに、変数の変更された値が読み取られます。 Volatile はアトミックな操作を実行するために簡単に悪用される可能性があります。
以下の例を見てみましょう。カウンターを実装すると、スレッドが開始されるたびに、counter inc メソッドが呼び出され、カウンターに 1 が追加されます。
実行環境 - jdkバージョン:jdk1.6.0_31、メモリ:3G CPU:x86 2.4G
次のようにコードをコピーします。
パブリック クラス カウンタ {
パブリック静的整数カウント = 0;
public static void inc() {
//結果を明確にするために、ここで 1 ミリ秒遅延します
試す {
Thread.sleep(1);
} catch (InterruptedException e) {
}
カウント++;
}
public static void main(String[] args) {
//同時に 1000 個のスレッドを開始して i++ 計算を実行し、実際の結果を確認します
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@オーバーライド
public void run() {
Counter.inc();
}
})。始める();
}
//ここの値は実行するたびに異なる可能性があり、おそらく 1000
System.out.println("実行結果: Counter.count=" + Counter.count);
}
}
実行結果: Counter.count=995
実際の操作結果は毎回異なる可能性があります。このマシンの結果は、実行結果: Counter.count=995 です。マルチスレッド環境では、Counter.count は結果が 1000 であることを期待していないことがわかります。
多くの人は、これはマルチスレッドの同時実行の問題だと考えています。この問題を回避するには、変数 count の前に volatile を追加するだけで済みます。その後、コードを変更して、結果が期待どおりかどうかを確認します。
次のようにコードをコピーします。
パブリック クラス カウンタ {
public volatile static int カウント = 0;
public static void inc() {
//結果を明確にするために、ここで 1 ミリ秒遅延します
試す {
Thread.sleep(1);
} catch (InterruptedException e) {
}
カウント++;
}
public static void main(String[] args) {
//同時に 1000 個のスレッドを開始して i++ 計算を実行し、実際の結果を確認します
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@オーバーライド
public void run() {
Counter.inc();
}
})。始める();
}
//ここの値は実行するたびに異なる可能性があり、おそらく 1000
System.out.println("実行結果: Counter.count=" + Counter.count);
}
}
実行結果: Counter.count=992
実行結果はまだ予想どおり 1000 に達していません。その理由を以下に分析してみましょう。
「Java ガベージ コレクション」の記事では、JVM 実行時のメモリの割り当てについて説明しています。メモリ領域の 1 つは、jvm 仮想マシン スタックです。各スレッドは、スレッドの実行中にスレッド スタックを保持します。スレッドがオブジェクトの値にアクセスするとき、スレッドはまずオブジェクト参照を通じてヒープ メモリに対応する変数の値を見つけ、次にヒープ メモリ変数の特定の値をスレッド ローカル メモリにロードして、そのコピーを作成します。その後、スレッドはヒープ メモリ内のオブジェクトの変数値とは関係なく、変更後の特定の時点 (スレッドが終了する前) にコピー変数の値を直接変更します。スレッド変数コピーの値は、ヒープ内のオブジェクトの変数に自動的に書き戻されます。このようにして、ヒープ内のオブジェクトの値が変化します。下の写真
このやりとりを説明してください
読み取りとロードは変数をメインメモリから現在の作業メモリにコピーします
実行コードの使用と割り当て、および共有変数値の変更
メインメモリ関連のコンテンツを作業メモリデータで保存および書き込みリフレッシュする
use と assign が複数回現れる場合があります
ただし、これらの操作はアトミックではありません。つまり、読み込みロード後にメイン メモリのカウント変数が変更された場合、スレッドの作業メモリの値はロードされているため変更されず、計算結果は期待どおりになります。同じ
volatile で変更された変数の場合、JVM 仮想マシンはメイン メモリからスレッドの作業メモリにロードされた値が最新であることのみを保証します。
たとえば、スレッド 1 とスレッド 2 が読み取りおよびロード操作中にメイン メモリ内のカウント値が両方とも 5 であることを検出した場合、最新の値をロードします。
スレッド 1 のヒープ数が変更されると、それがメイン メモリに書き込まれ、メイン メモリ内のカウント変数が 6 になります。
スレッド 2 はすでに読み取りおよびロード操作を実行しているため、操作の実行後、メイン メモリのカウント変数の値も 6 に更新されます。
その結果、volatile キーワードを使用して 2 つのスレッドが時間内に変更された後でも、同時実行性が維持されます。