Чувствительная операция в моей лаборатории сегодня прошла совсем не так. Привод на электронном микроскопе перешел ее границы, и после череды событий я потерял оборудование на 12 миллионов долларов. Я сузил более 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.
final
квалификатора (который не влияет на производимый байт-код) к полям x
и y
«решает» ошибку. Хотя это не влияет на байт-код, поля помечаются им, что заставляет меня думать, что это побочный эффект оптимизации JVM.
Point
p
которое удовлетворяет p.x+1 == p.y
, затем ссылка передается потоку опроса. В конце концов, поток опроса решает выйти, потому что считает, что условие не выполняется для одного из Point
s, которые он получает, но затем вывод консоли показывает, что он должен быть выполнен. Отсутствие volatile
здесь просто означает, что поток опроса может застрять, но это явно не проблема.
synchronized
делает ошибку не происходит? Это потому, что мне приходилось писать код случайным образом, пока я не нашел код, который воспроизводил бы это поведение детерминистически.