Aujourd'hui, une opération délicate dans mon laboratoire s'est complètement mal passée. Un actionneur sur un microscope électronique a dépassé ses limites et, après une chaîne d'événements, j'ai perdu 12 millions de dollars d'équipement. J'ai réduit plus de 40 000 lignes dans le module défectueux à ceci:
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);
}
}
Quelques échantillons de la sortie que je reçois:
$ 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
Puisqu'il n'y a pas d'arithmétique à virgule flottante ici, et nous savons tous que les entiers signés se comportent bien en cas de débordement en Java, je pense qu'il n'y a rien de mal à ce code. Cependant, malgré la sortie indiquant que le programme n'a pas atteint la condition de sortie, il a atteint la condition de sortie (il était à la fois atteint et non atteint?). Pourquoi?
J'ai remarqué que cela ne se produit pas dans certains environnements. Je suis sur OpenJDK 6 sur Linux 64 bits.
final
qualificatif (qui n'a aucun effet sur le bytecode produit) dans les champs x
et y
"résout" le bug. Bien que cela n'affecte pas le bytecode, les champs sont marqués avec lui, ce qui m'amène à penser que c'est un effet secondaire d'une optimisation JVM.
Point
p
est construit qui satisfait p.x+1 == p.y
, puis une référence est transmise au thread d'interrogation. Finalement, le thread d'interrogation décide de quitter, car il pense que la condition n'est pas satisfaite pour l'un des Point
s qu'il reçoit, mais la sortie de la console montre alors qu'il aurait dû être satisfait. L'absence d' volatile
ici signifie simplement que le fil d'interrogation peut rester bloqué, mais ce n'est clairement pas le problème ici.
synchronized
empêche le bug de se produire? C'est parce que j'ai dû écrire au hasard du code jusqu'à ce que j'en trouve un qui reproduise ce comportement de manière déterministe.