Una operación sensible en mi laboratorio hoy salió completamente mal. Un actuador en un microscopio electrónico superó sus límites, y después de una cadena de eventos perdí $ 12 millones en equipos. He reducido más de 40K líneas en el módulo defectuoso a esto:
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);
}
}
Algunas muestras de la salida que estoy obteniendo:
$ 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
Como no hay ninguna aritmética de coma flotante aquí, y todos sabemos que los enteros con signo se comportan bien en el desbordamiento en Java, creo que no hay nada de malo en este código. Sin embargo, a pesar de que la salida indica que el programa no alcanzó la condición de salida, alcanzó la condición de salida (¿se alcanzó y no se alcanzó?). ¿Por qué?
Me di cuenta de que esto no sucede en algunos entornos. Estoy en OpenJDK 6 en Linux de 64 bits.
final
calificador (que no tiene ningún efecto sobre el código de bytes producido) a los campos x
y y
"resuelve" el error. Aunque no afecta el código de bytes, los campos están marcados con él, lo que me lleva a pensar que esto es un efecto secundario de una optimización JVM.
Point
p
construye un A que satisface p.x+1 == p.y
, luego se pasa una referencia al hilo de votación. Finalmente, el hilo de sondeo decide salir porque cree que la condición no se cumple para uno de los Point
mensajes que recibe, pero luego la salida de la consola muestra que debería haberse cumplido. La falta de volatile
aquí simplemente significa que el hilo de votación puede atascarse, pero ese claramente no es el problema aquí.
synchronized
hace que el error no ocurra. Eso es porque tuve que escribir código al azar hasta que encontré uno que reprodujera este comportamiento de manera determinista.