作者说,由于读线程可能永远发现不了main对ready变量的修改就会永远循环下去,但是貌似不太可能永远读不到main对ready变量修改之后的值吧,可能会由于从栈区复制回堆区时,有几毫秒的误差,read线程读到的是旧值,但堆区值更新之后,read线程就读到了新值了吧,为什么说会永远循环下去呢?
再回去又读了一遍,有如下解释:
根据JVM设置,如果设置成server模式会有指令重排,如上述循环判断就会变成if(!ready){while(true){...}}所以就会造成死循环,如果设置成volatile,JVM就不会对这个变量进行指令重排。
亲测,线程是能读取到ready值的改变的,可以正常的结束
并且,看代码没有问题啊,应该不会出现读取不到ready值改变的情况的,一般通过旗标来关闭线程都是这样来操作的。
附上测试代码
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
除非线程没有去真正读取更新后的数据,比如一直读的寄存器等。那么就可能读取不到新数据
这是JAVA并发编程实践 第三章开头的一个例子,是并发编程的经典教材,按道理作者应该不会说错。
但是为何我在jdk1.7下出来的结果,以及根据我的理解,都不太可能永远死循环。
求合理解释
把打印方法注释,因为打印方法里存在synchronized代码块,在获得同步锁和释放同步锁的同时,会将工作内存刷入到主内存中。所以,会导致,不是用volatile关键字,该线程变量对其他线程变量也是可见的。仅是个人理解,有不周到之处请多多指教,谢谢。
package com.dsc.test;
public class VolatileTest {
public static void main(String args[]) throws InterruptedException {
SumThread sumThread = new SumThread();
new Thread(()->{
sumThread.run();
}).start();
Thread.sleep(1000);
sumThread.setController(false);
System.out.println("已经修改,结束运行");
}
static class SumThread {
private boolean controller=true;
public void setController(boolean controller) {
this.controller = controller;
}
public void run(){
while(controller){
// System.out.println();
/*
把打印方法注释,因为打印方法里存在synchronized代码块,在获得同步锁和释放同步锁的同时,
会将工作内存刷入到主内存中
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}*/
}
System.out.println("已经被修改");
}
}
}