我正在学习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”)
?
当您这样做时:
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
被调用,这将增加线程不是从缓存而是从主内存读取字段标志
的可能性。