考虑摘自 Joshua Bloch 的 Java Concurrency in Practice 一书中的片段 -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 而不是 number 的更新值?这是 重新排序 的一般含义吗?
同一本书页面底部的一段话揭示了对这一点的洞察——
这里有点困惑-
作者是说……只要在该线程中无法检测到重新排序……同时,他说-
- 即使重新排序对其他线程来说是明显的(清晰可见)。
如果以防万一,其他线程可以清楚地看到重新排序,为什么他同时说“只要在该线程内无法检测到重新排序”? 如果重新排序可见,则意味着它也可检测到 。不是吗?
最佳答案
这在一般情况下是不能保证的。此外,不能保证发生更新,因为没有 volatile
添加到字段之一。这将同步线程的缓存,并保证顺序。
(我希望我是对的。)
澄清(我希望)
给定的场景与 jvm 处理的 java 字节码无关。 (通常)编译器不会巧妙地重新排列或乱序解释字节码。它是在具有本地线程缓存的线程中运行的即时编译代码,重复保存公共(public)变量。
volatile 标记字段确保这些公共(public)变量与所有线程同步。当然,只要结果没问题,单个线程可以以任何顺序执行代码。
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 快。
如果 3. 和 4. 被 JIT 编译切换,同一个线程不会看到/检测到任何差异,但其他线程可能首先看到 y 的变化。那是没有
volatile
。关于java - 在其他/s之前的指令/语句是否保证首先执行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38694206/