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 Concurrency in Practice”,它可能会打印0,因为写入就绪可能会对读取器线程可见,然后写入数字或程序永远不会终止,因为它没有使用足够的同步。无法保证读取器线程可以看到主线程写入的ready和number的值。
怎么可能?程序将由一个线程顺序运行,它首先写入数字然后写入就绪变量。不是吗?这个程序怎么能永远循环呢?
答案 0 :(得分:1)
无法保证通过在一个线程中更改变量,其他线程将看到这些更改的结果。从技术上讲,它们之间没有发生过关系,因此没有保证(在实践中,你几乎会一直看到这些变化)。
这就是线程可以永远运行的原因。
其次,为什么有时为0?
好JLS说
写入数据竞争中的一个线程,并在另一个线程中读取 例如,线程可能看起来不按顺序发生。
这意味着你的
number = 42;
ready = true;
可以按任何顺序发生。现在,它们更有可能按顺序出现,但再次没有保证。
你可以通过将变量更改为易失性来修复它,在这种情况下,读者总是可以看到写入,或者通过使代码变为一个关键部分的状态(参见本书)。一般来说,我觉得使用太多的volatile变量有点像黑客,所以你应该谨慎使用它们,比如线程“运行”变量。
public class NoVisibility {
private static volatile boolean ready;
private static volatile 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;
}
}
答案 1 :(得分:0)
如果ready未标记为'volatile',则ReaderThread可能会检查其值(为0)。 它后来不再检查ready的值,因为它假设它没有改变。
volatile关键字指示编译器根本没有这样的假设,并且变量的值可能会在他的脚下发生变化。