[マルチスレッド] -volatileキーワードの特徴

1.volatileキーワードとは何ですか

毎日のインタビューで、揮発性は間違いなく非常に頻繁に言及される問題です。主要な公的口座も揮発性キーワードの分析でいっぱいです。では、揮発性とは何ですか?それらはどこで使用されますか?

まず、キーワードを理解するという点で、このキーワードの中国語の意味を理解することは私たちが覚えているのに役立つと思います。次に、Baidu Translateを開いてvolatileを検索すると、次の結果が得られます
ここに画像の説明を挿入
。BaiduTranslateを使用すると、volatileが主に変更しやすいものを形成するために使用される形容詞は、Java言語のキーワードとして、変数パラメーターを変更するためにしばしば使用されます。次に、次の質問は、揮発性の変更された変数によってどのような変更が生成されるかを理解する必要があります。つまり、volatileキーワードの役割は何ですか?

第二に、揮発性物質の導入

言うまでもありませんが、最初にデモを見てみましょう。

public class VolatileTest {
    
    
	private static boolean flag;
	// 违反可见性验证
	public static void main(String[] args) {
    
    
		flag = true;
		new Thread(() -> {
    
    
			try {
    
    
			//通过线程睡眠达到延时修改flag标记的作用
				Thread.currentThread().sleep(10l);
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
			flag = false;
		}).start();
		System.out.println("启动循环");
		// 如果上面flag修改生效 程序将退出循环
		while (flag) {
    
    
		}
		System.out.println("退出循环");
	}
}

実験結果:
ここに画像の説明を挿入
上記の実験結果は、新しく作成されたスレッドがフラグの値を変更したにもかかわらず、メインスレッドがそれを認識しないため、ループを終了できないことがわかります。ここでのポイントは、コードがないはずです。 whileループ、そうでない場合は結果に影響します。では、フラグを変更する価値があることをメインスレッドに認識させるにはどうすればよいでしょうか。推測する必要はありません。この章は揮発性についてです。上記の変更は、変数を宣言するときに次のように行われます。

	private  volatile static boolean flag;

はい、間違いではありません。プログラムの実行結果を変更するには、単語を追加するだけです。
ここに画像の説明を挿入
ここでは、プログラムが正常に終了したことがわかりますここで、volatileは何をしましたか?

三、揮発性の特徴

1.揮発性と可視性

可視性とは何ですか?加法性の簡単な説明は、スレッドが共有変数を変更すると、他のスレッドが変数変更のプロセスと変数の最新の状態を学習できるということです。
上記のデモでは、共有変数フラグにvolatileキーワードを追加しました。スレッドがフラグを変更しようとすると、JVMは他のスレッドに、以前に読み取った変数キャッシュの有効期限が切れており、共有メモリ領域に移動する必要があることを通知します。 。最新の変数値を取得します。

volatileの実現原理
についてスレッドがキャッシュを更新するために相互に通知する方法問題には、コンピューターの原理の知識の一部が含まれます。興味がある場合は、CPUのメモリバリアとキャッシュコヒーレンシプロトコルについて学ぶことができます。私は勝ちました。ここでは詳しく説明しません。

2.揮発性と原子性

前回のデモでvolatileが可視性を持っていることを証明しましたが、並行プログラミングの3つの機能の中でatomic volatileはサポートしていますか?言うまでもありませんが、デモにアクセスしてください。

public class VolatileAtomicity {
    
    
	public volatile int count = 0;

	private void addCount() {
    
    
	//count 自增
		for (int i = 0; i < 100; i++) {
    
    
			count++;
		}
		System.out.println("count = " + count);
	}

	public static void main(String[] args) {
    
    
		VolatileAtomicity volatileAtomicity = new VolatileAtomicity();
		// 循环创建1000个新线程 每个线程count自增100次 期望结果为100000
		for (int i = 0; i < 1000; i++) {
    
    
			new Thread(() -> {
    
    
				volatileAtomicity.addCount();
			}).start();
			;
		}
	}
}

期待される結果:100000
操作結果:

ここに画像の説明を挿入
ここでは、共有変数数にvolatileキーワードの変更を追加したものの、最終的な実行結果が期待を満たしていないことがわかります。なぜですか。実際、主な理由は、jvmが解析されたときにcount ++が彼を逆アセンブルしたことです。

  1. 最初にcountの値を読み取ります。現時点では、volatileには可視性があるため、countの値はこの時点での最新の値です。
  2. このとき、countの値が計算されます。この時点で他のスレッドがcountの値を変更している可能性があるため、この時点でのcount +1の値はスレッドセーフポリシーに違反していることに注意してください。
  3. count + 1の結果をcountに再割り当てします。この時点で、countの値は予想から外れてい
    ます。上記の例からわかるように、volatileは、読み取りに値する場合にのみ可視性を保証できますが、保証することはできません。後続の操作中の並行性。スレッドによって引き起こされるスレッドセーフの問題。したがって、volatileはアトミックではありません

ここで、volatileの可視性について補足説明行う必要があります。volatileの可視性は、スレッドが変数を読み取るときに最新の変数が取得され、操作の実行時に最新の変数が取得されないという事実に反映されます。読んだ後。メモリの読み取りと書き込みの障壁の概念設計のこの部分では、興味のある学生は自分でバイドゥをすることができます。

3.揮発性と秩序

順序を理解する前に、まずCPU命令の並べ替えという概念を理解する必要があります。

CPUは、コードを実行する際に、記述された順序で厳密にコードを実行するのではなく、as-if-serialの原則に基づいて適切に並べ替えた後、命令を実行します。これは主に、 CPUの実行効率を高め、アイドル時間を短縮します。

したがって、最初に行う必要があるのは、CPUに命令の並べ替えがあることを証明することです。デモについては話さないでください。

public class VolatileOrderliness {
    
    

	private static long a = 0;
	private static long b = 0;
	private static long c = 0;
	private static long d = 0;
	private static long e = 0;
	private static long f = 0;
	private static long g = 0;
	private static long h = 0;

	public static long count = 0;

	public static void main(String[] args) {
    
    
		// 由于cpu指令重排发生存在概率 所以使用死循环调用 然后再出现的时候通过break跳出循环
		for (;;) {
    
    
			a = 0;
			b = 0;
			c = 0;
			d = 0;
			e = 0;
			f = 0;
			g = 0;
			h = 0;
			count++;
			Thread t1 = new Thread(() -> {
    
    
				a = 1;
				c = 101;
				d = 102;
				g = b;
			});
			Thread t2 = new Thread(() -> {
    
    
				b = 1;
				e = 201;
				f = 202;
				h = a;
			});
			t1.start();
			t2.start();
			try {
    
    
				t1.join();
				t2.join();
				String result = "count = " + count + " g = " + g + ", h=" + h;
				if (g == 0 && h == 0) {
    
    
					// 当g 和h 都出现0的时候 一定是发生了指令重排序
					System.err.println(result);
					break;
				} else {
    
    
					System.out.println(result);
				}
			} catch (InterruptedException e1) {
    
    
				e1.printStackTrace();
			}

		}

	}

}


演算結果:
ここに画像の説明を挿入
まず、デモを分析します。スレッドt1が最初に実行されるか、スレッドt2が最初に実行されるかにかかわらず、コードの最初の行にはaとbに値1が割り当てられます。つまり、通常のコードロジックでは、スレッドによって最後に実行された割り当てgとhの値は1である必要がありますが、この時点でgとhの値は両方とも0であり、CPUがコードの実行順序を中断し、コードの実行時に順序を変更したことを示しています。次のように:
ここに画像の説明を挿入
ここに画像の説明を挿入
CPUはgとhの割り当て操作をaとbの割り当て操作に切り替えます。この問題を解決するのは実際には非常に簡単です。aとbにvolatileキーワードを追加するだけで済みます。

4、不安定な面接関連

ここでは、あなたと共有するためにいくつかの一般的な不安定な面接の質問も要約しました。

1.どのシナリオが通常volatileに適用されるかについて話します
回答:volatileの最も単純なアプリケーションシナリオは、ダブルチェックロックモードで実装されたシングルトンオブジェクトで使用することです。DCLシングルトンモードでは、volatileは主にスレッドが取得するのを防ぐために使用されますit。命令の並べ替えによって引き起こされる半初期化された変数。
2. volatileとsynchronizedの類似点と相違点は何ですか?
回答:
1)Volatileは主に変数の変更に使用され、synchronizedは主にコードブロックの変更に使用されます。
2)揮発性は原子性を保証できません、同期はできます
3)揮発性はスレッドの閉塞を引き起こさず、同期は引き起こします

質問がある場合、または作者が間違っていると思われる場合は、メッセージを残してください。
幸運を!

おすすめ

転載: blog.csdn.net/xiaoai1994/article/details/110437416