今日の私の研究室での敏感な操作は完全に間違っていました。電子顕微鏡のアクチュエーターはその境界を越え、一連の出来事の後に私は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の 64ビットLinux上の6。
final
修飾子(生成されたバイトコードには影響しません)をフィールドに追加し、バグx
をy
「解決」します。バイトコードには影響しませんが、フィールドにはフラグが立てられているため、JVM最適化の副作用であると思います。
Point
p
これを満たす構成されp.x+1 == p.y
、その後、参照がポーリングスレッドに渡されます。最終的に、ポーリングスレッドPoint
は、受信したの1つで条件が満たされていないと判断して終了することを決定しますが、コンソール出力は、条件が満たされているはずであると示します。ここにないことは、volatile
単にポーリングスレッドがスタックする可能性があることを意味しますが、それは明らかにここでは問題ではありません。
synchronized
を削除するとバグが発生しないことに注意してください。これは、この動作を確定的に再現するコードが見つかるまで、ランダムにコードを記述しなければならなかったためです。