为什么这个Java程序终止,尽管这显然应该在’t (and didn’ t)?

问题:

今天实验室的敏感操作完全错了。电子显微镜上的执行器超过了边界,经过一连串的事件,我失去了1200万美元的设备。我在错误的模块中缩小了40K行以下:

import java.util.*;

class A {
    static Point currentPos = new Point(1,2);
    static class Point {
        int x;
        int y;
        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    public static void main(String[] args) {
        new Thread() {
            void f(Point p) {
                synchronized(this) {}
                if (p.x+1 != p.y) {
                    System.out.println(p.x+" "+p.y);
                    System.exit(1);
                }
            }
            @Override
            public void run() {
                while (currentPos == null);
                while (true)
                    f(currentPos);
            }
        }.start();
        while (true)
            currentPos = new Point(currentPos.x+1, currentPos.y+1);
    }
}

我得到的输出的一些样本:

$ java A
145281 145282
$ java A
141373 141374
$ java A
49251 49252
$ java A
47007 47008
$ java A
47427 47428
$ java A
154800 154801
$ java A
34822 34823
$ java A
127271 127272
$ java A
63650 63651

由于这里没有任何浮点算术,我们都知道有符号的整数在Java中的溢出上表现良好,我认为这个代码没有任何问题。然而,尽管输出表明程序没有达到退出条件,但是到达退出条件(达不到)。为什么?
我注意到这在某些环境中不会发生。我在64位Linux上的OpenJDK 6。

回答:

显然,对currentPos的写入不会发生,在读取之前,但是我看不出这是怎么回事。

 currentPos = new Point(currentPos.x+1, currentPos.y+1);做了一些事情,包括将默认值写入xy(0),然后在构造函数中写入它们的初始值。由于您的对象未被安全地发布,所以这4个写操作可以由编译器/ JVM自由重新排序。
所以从读取线程的角度来看,使用其新值y读取x的默认值为0的合法执行是例如。当你到达println语句(通过同步方式,因此影响读取操作)时,变量具有初始值,并且程序打印预期值。
currentPos标记为volatile将确保安全发布,因为您的对象实际上是不可变的 – 如果在实际使用情况下,对象在构建后被突变,则volatile保证将不够,您可以再次看到不一致的对象。
或者,您可以使Point不变,即使不使用volatile也可以确保安全发布。要实现不变性,只需要标记xy final。
作为附注,如上所述,synchronized(this) {}可以被JVM视为无操作(我理解你包含它来重现行为)。

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Why does this Java program terminate despite that apparently it shouldn’t (and didn’t)?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

2 + 2 =