在什么体系结构/操作系统中,其他线程可以在构造函数调用后看到默认的非最终字段值?

我试图在非最终字段的对象初始化不足的情况下重现内存可见性问题(JLS 17.5 Final Field SemanticsFinalFieldExample类示例)。其中指出“但是,f.y不是最终的;相反,f.y不是最终的。因此,不能保证 reader() 方法能够看到它的值 4”

我试过这个代码:

public class ReorderingTest2 {


    public static void main(String[] args) {
        for (int i = 0; i < 2500; i++) {
            new Thread(new Reader(i)).start();
            new Thread(new Writer(i)).start();
        }
    }

    static class Reader implements Runnable {
        private String name;

        Reader(int i) {
            this.name = "reader" + i;
        }

        @Override
        public void run() {
            //System.out.println(name + " started");
            while (true) {
                FinalFieldExample.reader(name);
            }
        }
    }

    static class Writer implements Runnable {
        private String name;

        Writer(int i) {
            this.name = "writer" + i;
        }

        @Override
        public void run() {
            //System.out.println(name + " started");
            while (true) {
                FinalFieldExample.writer();
            }
        }
    }

    static class FinalFieldExample {
        int x;
        int y;
        static FinalFieldExample f;

        public FinalFieldExample() {
            x = 3;
            y = 4;
        }

        static void writer() {
            f = new FinalFieldExample();
        }

        static void reader(String name) {
            if (f != null) {
                int i = f.x;
                int j = f.y;
                if (i != 3 || j != 4) {
                    System.out.printf("reader %s sees it!%n", name);
                }
            }
        }
    }

}

之前的类似主题一样 - 我已经在Windows的不同PC(从2到8核)上尝试过,甚至在我们的服务器端Solaris 32核盒上 - 我无法重现它:f.x和f.y - 总是已经正确初始化。

对于我得到答案的英特尔/x86/x64 架构 - 它们几乎具有默认的 memery 保证,可防止此类构造函数逻辑重新排序。似乎 Solaris/sparc 也是如此?

那么,在什么体系结构/操作系统中可以再现这种重新排序呢?


答案 1

阿尔法。Paul E. McKenney的书《并行编程很难,如果是这样,你能做些什么?》有一章解释了最重要平台的主题模型。


答案 2

为了获得所需的结果,您可以尝试打开繁重优化,因此请在模式下运行程序。-server

我首先想到的是制作,但这显然会搞砸整个实验。fvolatile

打开实时编译器的 XML 日志记录(如果您使用的是 HotSpot JVM),并查看生成的机器代码(使用某些外部调试器或内存转储器)。然后,您可以检查生成的代码,如果这甚至可以让您观察所需的结果。