为什么这个Java程序终止,尽管显然它不应该(而没有)?

Java并发谜题
探讨一个导致昂贵设备损坏的Java并发程序错误案例。该程序看似无误却意外终止,揭示了内存可见性和线程安全的问题。
问题

今天我的实验室的敏感操作完全错误.电子显微镜上的致动器越过其边界,在一系列事件后,我损失了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.



解决方法

Obviously the write to currentPos doesn't happen-before the read of it, but I don't see how that can be the issue.

currentPos = new Point(currentPos.x + 1,currentPos.y + 1); 做一些事情,包括将默认值写入 x  y (0),然后在构造函数中写入它们的初始值.由于你的对象不安全发布,这4个写操作可以由编译器/ JVM自由重新排序.

因此,从读取线程的角度来看,使用其新值读取 x 是合法的执行,例如 y ,其默认值为0.当您到达 println 语句(顺便说一句,它是同步的,因此影响读操作),变量有它们的初始值,程序打印预期值.

currentPos 标记为 volatile 将确保安全发布,因为您的对象实际上是不可变的 - 如果在您的实际使用情况下对象在构造之后突变, / code>保证将不够,您可能再次看到不一致的对象.

或者,您可以使 Point 不可变,这也将确保安全发布,即使不使用 volatile .为了实现不变性,你只需要标记 x y final.

作为注释和前面已经提到的, synchronized(this){} 可以被JVM视为无操作(我理解你包含它来重现行为) p>




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值