gpt4 book ai didi

java - 为什么这个 Java 程序会终止,尽管它显然不应该(也不应该)?

转载 作者:IT老高 更新时间:2023-10-28 12:16:42 26 4
gpt4 key购买 nike

今天我实验室的一项敏感操作完全出错了。电子显微镜上的一个致动器越过了它的边界,在一系列事件之后,我损失了 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 中溢出时表现良好,我认为这段代码没有任何问题。然而,尽管输出表明程序没有达到退出条件,但它达到了退出条件(既达到了没有达到?)。为什么?


我注意到这在某些环境中不会发生。我在 OpenJDK 6 在 64 位 Linux 上。

最佳答案

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 将确保安全发布,因为您的对象实际上是不可变的 - 如果在您的实际用例中,对象在构造后发生了变异,volatile 保证是不够的,您可能会再次看到不一致的对象。

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

作为旁注,如前所述,synchronized(this) {} 可以被 JVM 视为无操作(我知道您包含它是为了重现行为)。

关于java - 为什么这个 Java 程序会终止,尽管它显然不应该(也不应该)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16159203/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com