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。
final
关键字(不影响产生的字节码)添加到字段x
和y
中,“解决”了这个bug。尽管它不影响字节码,但这些字段会被标记上final
,这让我想到这是JVM优化的一个副作用。 - Niv Steingartenp.x+1 == p.y
的Point
p
,然后将其 引用 传递给轮询线程。最终,轮询线程决定退出,因为它认为其中一个接收到的Point
不满足条件,但是控制台输出显示它应该已经满足条件。在这里缺少volatile
意味着轮询线程可能会被卡住,但这显然不是问题的原因。 - Erma K. Pizarrosynchronized
会使错误不会发生?这是因为我必须随机编写代码,直到找到可以确定重现此行为的代码。 - Dog