考虑一下摘自 Joshua Bloch实践中的Java Concurrency -
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; // Statement 1
ready = true; // Statement 2
}
}
对于由JVM启动的主线程,是否保证语句1 将在语句2 之前执行。
我完全理解ReaderThread可能无法看到上述两个静态变量的更新值。我不是要求解决方案。但是如果声明1在声明2之前执行,那么ReaderThread是否仍然可以看到 ready &的更新值。不是数字?这是重新排序一般意味着什么?
同一本书页面底部的一个段落揭示了对此的见解 -
无法保证将在一个线程中执行操作 按程序给出的顺序,只要重新排序不是 可以从该线程中检测到 - 即使重新排序是明显的 到其他线程。
这里有点混乱 -
作者说... 只要在该线程中无法检测到重新排序...... 同时,他说 -
- 即使重新排序对其他线程显而易见(清晰可见)。
如果以防万一,重新排序对其他线程清晰可见,他为什么同时说“只要在该线程内无法检测到重新排序”? 如果重新排序可见,则表示它也可以检测到。不是吗?
答案 0 :(得分:2)
一般情况下不保证。此外,无法保证更新发生,因为没有volatile
添加到其中一个字段。这将同步线程的缓存,并保证顺序。
(我希望我是对的。)
澄清(我希望)
给定的场景并不像jvm处理的java字节码那么重要。 (通常)不是这样编译器巧妙地重新排列或解释字节代码。它是在具有本地线程缓存的线程中运行的即时编译代码,重复地保存公共变量。
volatile标记字段可确保将这些公共变量同步到所有线程。当然,只要结果合适,单个线程就可以按任何顺序执行代码。
y = ++x;
以下伪装配的实际执行
1. move from @x to register1
2. increment register1
3. move from register1 to @x
4. move from register1 to @y
5. synchronize @x and @y
可能在不同的处理器上完全不同。一个或两个变量可能会缓存在线程内存本身中,或者需要写入far变量,或者不需要。
当然可以保证处理相同的线程可以得到正确的结果。没有人看到,顺序是无关紧要的:由于记忆的原因,4可能会在3之前变得比3更快。
如果3.和4.被JIT编译切换,同一个线程将看不到/检测到任何差异,但其他线程可能首先看到y的变化。那是没有volatile
。
这一切都非常深奥,太低级了。有人可能会想到它出现在语言规范中,就像
byte
变量在内部存储在一个4字节字中一样。它处理实现问题,确实是相关的,比如缺少字节操作。当一个人对这个主题感兴趣时,拿一个汇编程序,也许和C结合起来试试这些东西吧。否则远离不安全的编程。