提问者:小点点

Java多线程变量可见性问题[重复]


我正在学习Java的易失性,我的代码是这样的。

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("abc"),t1线程将读取新值并停止循环。

我知道如何使用volatile来解决可见性问题,所以我的问题是:

为什么当我写System.out时,t1可以读取标志的新值。while循环中的println(“abc”)


共1个答案

匿名用户

当您这样做时:

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

您将具有空体的循环,这可能导致在场上旋转所知道的;从源头可以读到:

在空循环语句的条件下重复读取非易失性字段可能会导致无限循环,因为编译器优化可能会将此字段访问移出循环。

但是,通过添加语句< code > system . out . println(flag);在你的循环中,你要避开旋转磁场。尽管如此,如果线程< code>t1从其高速缓存中读取值为< code>true的字段< code>flag,则该线程将仅在存储该字段值的高速缓存行无效时停止,并且该线程从主存储器中获得新的更新值< code>flag字段(即< code>flag=false)。

除其他事项外,易失性可确保线程将读取标志字段的最大更新值。因此,使字段标志可变可确保:

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

潜在的编译器优化不会将对字段标志的访问移出循环。

但是,如果我在 while 循环中编写一些代码,如 System.out.println(“abc”),则线程 t1 将读取新值并停止循环。

这是双重的:首先,通过添加< code > System.out.println(" ABC ")您将避免自旋场问题;其次,如果查看system . out . println代码:

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

您可以看到有一个synchronized被调用,这将增加线程不是从缓存而是从主内存读取字段标志的可能性。

相关问题