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 63651Puisqu'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.
finalqualificatif (qui n'a aucun effet sur le bytecode produit) dans les champs xet 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 pest 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 Points qu'il reçoit, mais la sortie de la console montre alors qu'il aurait dû être satisfait. L'absence d' volatileici signifie simplement que le fil d'interrogation peut rester bloqué, mais ce n'est clairement pas le problème ici.
                synchronizedempê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.