ホームページ  >  記事  >  Java  >  Java の Volatile キーワードはスレッドの安全性を保証しますか?

Java の Volatile キーワードはスレッドの安全性を保証しますか?

王林
王林転載
2023-04-21 20:07:062841ブラウズ

volatile

volatile は Java では比較的重要なキーワードで、主に、さまざまなスレッドによってアクセスおよび変更される変数を変更するために使用されます。

この変数は 2 つの特性のみを保証できます。1 つは秩序性の確保、もう 1 つは可視性の確保です。

それでは、秩序とは何であり、可視性とは何でしょうか?

秩序性

それでは、秩序とは何でしょうか?

実際、プログラムの実行順序はコードの順序に基づいており、命令の並べ替えは禁止されています。

当たり前のことのように思えますが、実際はそうではありません。命令の並べ替えは、JVM が命令を最適化し、プログラムの実行効率を向上させ、シングルスレッドの実行結果に影響を与えることなく可能な限り並列性を高めるためのものです。プログラム。

ただし、マルチスレッド環境では、一部のコードの順序が変更され、論理エラーが発生する可能性があります。

そして、volatile はこの機能によりよく知られています。

volatile はどのようにして秩序性を確保するのでしょうか?

多くの友人は、インターネットで言われていることは、volatile は命令の並べ替えを禁止することができ、コード プログラムが厳密にコードの順序で実行されることを保証する、と言っています。これにより秩序が確保されます。 volatile によって変更された変数に対する操作は、厳密にコードの順序で実行されます。つまり、volatile によって変更された変数に対してコードが実行されるときは、その前のコードが実行され、その後ろのコードが実行される必要があります。実行されないこと。

インタビュアーがこの時点でさらに深く掘り下げ続けなかった場合、おめでとうございます。この質問は答えられたかもしれませんが、インタビュアーがさらに深く掘り下げ続けた場合、なぜコマンドの再配置が禁止されているのでしょうか? それはコマンドの再配置ですか? ?

ソース コードから命令までの実行は、通常、図に示すように 3 つの並べ替えに分けられます。次に、volatile が命令の並べ替えをどのように禁止しているかを確認する必要があります。

コードを直接使用して検証します: Java の Volatile キーワードはスレッドの安全性を保証しますか?

public class ReSortDemo {

int a = 0;
boolean flag = false;

public void mehtod1(){
a = 1;
flag = true;
}

public void method2(){
if(flag){
a = a +1;
System.out.println("最后的值: "+a);
}
}
}

誰かがこのコードを見たら、間違いなく「このコードの結果はどうなるでしょうか?」と言うでしょう。

一部の人は 2 だと言っています。はい、単一のスレッドでのみ呼び出した場合、結果は 2 になりますが、複数のスレッドで呼び出された場合、最終的な出力結果は必ずしも私たちが想像した 2 になるとは限りません。これを行うときは、両方の変数を volatile に設定する必要があります。 シングルトン モードについて詳しく知っているなら、この揮発性ファイルに注目したはずです。

次のコードを見てみましょう:

class Singleton {
// 不是一个原子性操作
//private static Singleton instance;
//改进,Volatile 可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!
private static volatile Singleton instance;

// 构造器私有化
private Singleton() {
}

// 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题,同时保证了效率, 推荐使用
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

上記のシングルトン パターンをご存知ですか?

はい、これは **ダブル チェック (DCL 遅延スタイル) **

一部の人は、命令の並べ替えが存在するため、両端の検索メカニズムはそうであると言うでしょう。スレッドセーフである必要があるため、スレッドセーフにするために synchronized キーワードが使用されます。 可視性

実際、可視性は、共有変数への変更がマルチスレッド環境の他のスレッドに即座に見えるかどうかの問題です。

それでは、彼の可視性は一般的にどこに現れるのでしょうか?どこで使われていますか?

実際、この変数は一般に、この変数の値を決定するループがあり、スレッドがこのパラメータを変更するように定義されたグローバル変数など、その可視性を確保するために主に使用されます。時間が来ると、このループは停止し、次の実行にジャンプします。

揮発性変更を使用しないコード実装を見てみましょう:

public class Test {

private static boolean flag = false;

public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A开始执行:");
for (;;){
if (flag){
System.out.println("跳出循环");
break;
}
}
}
}).start();
Thread.sleep(100);

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B开始执行");
flag = true;
System.out.println("标识已经变更");
}
}).start();
}
}

結果は間違いなくご想像のとおりです。

実行結果は次のようになります:

スレッド A の実行開始:

スレッド B の実行開始ロゴが変更されました

確かに、これはそうです。それ。



#volatile を使用すると、このコードの実行結果は異なりますか?

試してみましょう: Java の Volatile キーワードはスレッドの安全性を保証しますか?

public class Test {

private static volatile boolean flag = false;

public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A开始执行:");
for (;;){
if (flag){
System.out.println("跳出循环");
break;
}
}
}
}).start();
Thread.sleep(100);

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B开始执行");
flag = true;
System.out.println("标识已经变更");
}
}).start();
}

このようにして、別の実行結果を確認し、ループ内の出力ステートメントを実行できます。

つまり、スレッド B でこの変更された変数を変更し、最後にスレッド A でデータ情報を正常に読み取ることができます。

アトミック性は保証できますか?

Java の Volatile キーワードはスレッドの安全性を保証しますか?いいえ、コードを見てみましょう。volatile によって変更された変数です。

public class Test {

// volatile不保证原子性
// 原子性:保证数据一致性、完整性
volatile int number = 0;

public void addPlusPlus() {
number++;
}

public static void main(String[] args) {
Test volatileAtomDemo = new Test();
for (int j = 0; j < 20; j++) {
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
volatileAtomDemo.addPlusPlus();
}
}, String.valueOf(j)).start();
}// 后台默认两个线程:一个是main线程,一个是gc线程
while (Thread.activeCount() > 2) {
Thread.yield();
}
// 如果volatile保证原子性的话,最终的结果应该是20000 // 但是每次程序执行结果都不等于20000
System.out.println(Thread.currentThread().getName() +
" final number result = " + volatileAtomDemo.number);
}
}

アトミック性が保証できる場合、最終的なresult は 20000 である必要がありますが、最終結果が毎回 20000 になるとは限りません。例:

##main Final Number result = 17114

main Final Number result = 20000

メインの最終数値結果 = 19317

3 回の実行で、すべて異なる結果が得られました。

なぜこれが起こるのでしょうか?これは番号
と関係があります

number は 3 つの命令に分割されます

  • GETFIELD を実行してメイン メモリ内の元の値番号を取得します

  • IADD を実行して 1 を加算します操作

  • PUTFIELD を実行し、作業メモリ内の値をメイン メモリに書き戻す

複数のスレッドが PUTFIELD 命令を同時に実行すると、メインメモリへの書き戻しの問題になるため、最終結果は 20000 にならないため、volatile はアトミック性を保証できません。

以上がJava の Volatile キーワードはスレッドの安全性を保証しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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