尽管没有代码明确泄漏,未初始化的对象泄露给另一个线程?

Dog*_*Dog 4 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

主线程

主线程创建的实例Bx设置为1,则该实例写入静态字段A.b.它永远重复这个动作.

投票线程

生成的线程轮询直到它发现A.b.x不是1.

?!?

有一半的时间它按预期进入无限循环,但有一半时间我得到这个输出:

$ java A
0
Run Code Online (Sandbox Code Playgroud)

为什么轮询线程能够看到一个B已经x不设置为1?


x%2而不仅仅是x因为这个问题是可以重现的.


我在linux x64上运行openjdk 6.

Eug*_*ene 10

这就是我的想法:因为b不是最终的,编译器可以自由地重新排序操作,对吧?因此,这基本上是一个重新排序的问题,因此不安全的发布问题将变量标记为final将解决问题.

或多或少,它与Java内存模型文档中提供的示例相同.

真正的问题是这是如何可能的.我也可以在这里推测(因为我不知道编译器将如何重新排序),但是在写入x之前,可能会将对B的引用写入主存储器(其他线程可见).在这两个操作之间,读取发生,因此为零值