java多线程变量可见性问题

nfeuvbwi  于 2021-07-03  发布在  Java
关注(0)|答案(1)|浏览(427)

这个问题在这里已经有答案了

如果没有print语句,循环看不到其他线程更改的值(1个答案)
与system.out[duplicate](3个答案)关联的java线程的奇怪行为
25天前关门了。
我正在学习java的volatile,我的代码是这样的。

public class Test {
    public static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            while(flag){
                // System.out.println(flag);
            }
            System.out.println("end");
        });
        t1.start();

        Thread.sleep(100);

        Thread t2 = new Thread(()->{
            flag = false;
            System.out.println("changed");
        });
        t2.start();
    }
}

我知道如果flag没有volatile,线程就不存在了。这是一个可见性问题。
但是如果我在while循环中编写一些代码,比如 System.out.println("") ,t1线程将读取新值并停止循环。
我知道如何使用volatile解决可见性问题,所以我的问题是:
为什么t1在写入时可以读取flag的新值 System.out.println("") 在while循环中?

jexiocij

jexiocij1#

当您这样做时:

while(flag){
     // System.out.println(flag);
}

你将有一个空体的环,这可以导致通过自旋场所知道的东西;从源头上可以看出:
在空循环语句的条件下重复读取非易失性字段可能会导致无限循环,因为编译器优化可能会将此字段访问移出循环。
但是,通过添加语句 System.out.println(flag); 在你的循环中你要避免旋转场。尽管如此,如果 t1 读这个领域 flag 从缓存中获取一个值 true ,仅当存储该字段值的缓存线失效,并且线程获得新的更新值时,线程才会停止 flag 字段(即。, flag=false )从主存储器。
这个 volatile 确保线程读取的更新值最多 flag 现场。因此
flag volatile 确保:

while(flag){
    // System.out.println(flag);
}

潜在的编译器优化不会移动对字段的访问 flag 出局了。
但是如果我在while循环中编写一些代码,比如system.out.println(“”),线程t1将读取新值并停止循环。
通过添加 System.out.println("") 您将避免旋入字段问题,其次,如果查看system.out.println代码:

public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

你可以看到有一个 synchronized 这将增加线程读取字段的可能性 flag 不是从缓存而是从主内存。

相关问题