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

时间:2011-06-09 14:50:22

标签: java multithreading constructor processor-architecture memory-visibility

我正在尝试在非最终字段(JLS 17.5 Final Field SemanticsFinalFieldExample类示例)的对象初始化不足的情况下重现内存可见性问题。它声明“但是,f.y不是最终的;因此读者()方法不能保证看到它的值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);
                }
            }
        }
    }

}

previous my similar topic一样 - 我尝试过使用Windows的不同PC(2到8个核心)甚至是我们服务器端的Solaris 32核心盒 - 我无法重现它:fx和fy - 总是已经适当初始化了。

对于作为I got the answer的Intel / x86 / x64架构 - 它们几乎default memery guarantees阻止了这种构造函数逻辑重新排序。 对Solaris / sparc来说似乎也是如此吗?

那么在哪种架构/操作系统中可以重现这种重新排序?

4 个答案:

答案 0 :(得分:1)

α。 Paul E. McKenney的书Is Parallel Programming Hard, And, If So, What Can You Do About It?有一章解释了最重要平台的主题模型。

答案 1 :(得分:0)

要获得所需的结果,您可能会尝试启用大量优化,因此请以-server模式运行该程序。

我首先考虑制作f volatile,但这显然会影响整个实验。

打开即时编译器的XML日志记录(如果您使用的是HotSpot JVM),并查看生成的机器代码(使用一些外部调试器或内存转储器)。然后你可以检查生成的代码,如果它甚至允许你观察你想要的结果。

答案 2 :(得分:0)

我建议您获取“Java Concurrency in Practice”的副本,并阅读第3章,其中详细介绍了JVM对锁定和可见性的保证。你的问题与特定的体系结构无关,所有与理解相关的事情都发生在Java之前。

我认为你不能重复这个问题,因为在FinalFieldExample构造函数的末尾有一个发生前的边缘,它保证x = 3和y = 4;

顺便说一句。 FinalFieldExample对象有点混乱。它想成为一个合适的单身人士,但你没有这样编码。静态“f”周围缺乏同步使得理解这个类的运行时行为更加困难。 Methinks它应该是一个合适的单例,同步保护对静态“f”的访问,你应该调用编写器和读者方法,如...

FinalFieldExample.getInstance()作家();

只是说'

答案 3 :(得分:0)

这应该是一个单独的问题......但它非常重要。这是我之前发表的评论的更广泛的版本。

jls第17.4节的第一部分说:

  

为了确定执行中的线程t的动作是否合法,我们简单地说   评估线程t的实现,因为它将在单个中执行   线程上下文,如本规范其余部分所定义。

我被挂断的地方是理解“在本规范其余部分中定义的”对于程序顺序的意义。

在手头的情况下,分配

f = new FinalFieldExample();

受赋值语义(第15.26.1节)的约束,其中以下属于。它在规范中是混乱的错误格式(特别是第三步),我相信我已经重新格式化它以准确反映意图。

[否则,]需要三个步骤

  1. 首先,评估左侧操作数以生成变量。如果此评估突然完成,则赋值表达式出于同样的原因突然完成;不评估右侧操作数,也不进行任何分配。
  2. 否则,将评估右侧操作数。如果此评估突然完成,则赋值表达式会因同样的原因突然完成,并且不会发生任何分配。
  3. 否则,将右侧操作数的值转换为左侧变量的类型,进行值集转换(§5.1.13)到相应的标准值集(不是扩展) -exponent value set),转换结果存储在变量中。
  4. 这对我来说就像单线程“程序顺序”的规范。我误解了什么?

    或许,答案是真正意图的是“鸭子测试” - 如果单个线程执行就像所有内容都按照指定的顺序完成,那么这是一个正确的实现。但是这一部分的编写方式与使用出现一词清楚明确的其他地方有很大不同,例如:

      

    Java编程语言还保证在操作的任何部分之前,操作符的每个操作数(条件运算符&amp;&amp;,||和?:) 的每个操作数都完全评估本身已经完成。