java - 尽管没有代码明确泄漏未初始化的对象泄漏到另一个线程?

标签 java concurrency initialization memory-visibility

让我们看看这个简单的 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/

相关文章:

language-agnostic - 复制垃圾收集器如何确保复制时不会访问对象?

ios - 检查 UICollectionViewCell 是否已创建以避免重用

Java变量设置

networking - Linux网络驱动程序中的并发: probe() VS ndo_open(), ndo_start_xmit() VS NAPI poll()

java - 如何返回菜单?

java - 在单个流中工作的多个线程

swift - 自动初始化器继承的条件是初始化器的签名

c++ - 为什么 isocpp 允许我们超过枚举类值?

java - findViewById 出现问题,用图表解释

java - 我可以使用 POI 从浏览器编辑 Excel 文件吗?