让我们看看这个简单的 Java 程序:
import java.util.*;
class A {
static B b;
static class B {
int x;
B(int x) {
this.x = x;
}
}
public static void main(String[] args) {
new Thread() {
void f(B q) {
int x = q.x;
if (x != 1) {
System.out.println(x);
System.exit(1);
}
}
@Override
public void run() {
while (b == null);
while (true) f(b);
}
}.start();
for (int x = 0;;x++)
b = new B(Math.max(x%2,1));
}
}
主线程
主线程创建一个 B
的实例,将 x
设置为 1,然后将该实例写入静态字段 A.b
。它永远重复这个 Action 。
轮询线程
生成的线程轮询,直到发现 A.b.x
不为 1。
?!?
一半的时间它进入预期的无限循环,但一半的时间我得到这个输出:
$ java A
0
为什么轮询线程能够看到 x
未设置为 1 的 B
?
x%2
而不是 x
在这里只是因为问题可以重现。
我在 linux x64 上运行 openjdk 6。
最佳答案
这是我的想法:因为b 不是final,所以编译器可以随意对操作重新排序,对吧?所以这从根本上说是一个重新排序问题,因此是一个不安全的发布问题将变量标记为 final 将解决这个问题。
或多或少,它与 Java memory model docs 中提供的示例相同。 .
真正的问题是这怎么可能。我也可以在这里推测(因为我不知道编译器将如何重新排序),但也许在写入 x 之前,对 B 的引用已写入主内存(其他线程可见)。在这两个操作之间发生读取,因此为零值
关于java - 尽管没有代码明确泄漏未初始化的对象泄漏到另一个线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16178020/