java sleep clock

Error de Java en suspensión() al cambiar la hora del sistema operativo: ¿alguna solución?



sleep clock (5)

El error que me molesta es el mismo que este ticket . Básicamente, si cambia el reloj del sistema operativo a una fecha en el pasado, no se activará todo el hilo que estaba durmiendo en el momento del cambio.

La aplicación que estoy desarrollando está pensada para funcionar 24/24, y nos gustaría poder cambiar la fecha del sistema operativo sin detenerla (por ejemplo, para cambiar de horario de verano a invierno). Lo que sucede por el momento es que cuando cambiamos la fecha al pasado, algunas partes de la aplicación simplemente se congelan. Observé que en varias máquinas, en Windows XP y Linux 2.6.37, y con una JVM reciente (1.6.0.22).

Probé muchas primitivas de Java, pero todas tienen el mismo comportamiento:

  • Thread.sleep (largo)
  • Thread.sleep (long, int)
  • Object.wait (largo)
  • Object.wait (long, int)
  • Thread.join (largo)
  • Thread.join (long, int)
  • LockSupport.parkNanos (largo)
  • java.util.Timer
  • javax.swing.Timer

Ahora, estoy fuera de idea para solucionar este problema. Creo que no hay nada que pueda hacer para evitar que los hilos de dormir se congelen. Pero me gustaría, al menos, advertir al usuario cuando se detecte un cambio peligroso en el reloj del sistema.

Se me ocurrió un hilo de monitoreo que detecta tales cambios:

Thread t = new Thread(new Runnable() { @Override public void run() { long ms1 = System.currentTimeMillis(); long ms2; while(true) { ms2 = ms1; ms1 = System.currentTimeMillis(); if (ms1 < ms2) { warnUserOfPotentialFreeze(); } Thread.yield(); } } }); t.setName("clock monitor"); t.setPriority(Thread.MIN_PRIORITY); t.setDaemon(true); t.start();

El problema es que esto hace que la aplicación crezca del 2% de uso de la CPU al 15% cuando está inactiva.

¿Tiene una idea para solucionar el problema original, o puede pensar en otra forma de controlar la apariencia de la congelación de hilos?

Editar

Ingo sugirió no tocar el reloj del sistema. Estoy de acuerdo en que generalmente no es necesario. El problema es que no controlamos lo que nuestros clientes hacen con sus computadoras (planeamos vender cientos de copias).

Peor aún: una de nuestras máquinas presenta este problema sin ninguna intervención manual. Supongo que el sistema operativo (Windows XP) sincroniza regularmente su reloj con el reloj RTC, y esto hace que el reloj del sistema operativo retroceda en el tiempo de forma natural.

Epílogo

Descubrí que algunas afirmaciones en mi pregunta estaban equivocadas. En realidad, hay dos causas separadas involucradas en mi problema inicial. Ahora, puedo decir dos cosas con seguridad:

  1. Solo en mi máquina (archlinux con kernel 2.6.37 con un OpenJDK 64 bits 1.6.0_22), Thread.sleep , Object.wait , Thread.join , LockSupport.parkNanos tienen el mismo problema: se activan solo cuando llega el reloj del sistema El tiempo "objetivo" de despertar. Sin embargo, un simple sleep en mi cáscara no presenta el problema.

  2. En todas las máquinas que probé (incluida la mía), java.util.Timer y java.swing.Timer tienen el mismo problema (se bloquean hasta que se alcanza el tiempo "objetivo").

Entonces, lo que he hecho es que reemplazé todo el Timer de Java por una implementación más simple. Esto resuelve el problema para todas las máquinas, pero la mía (solo espero que mi máquina sea una excepción más que una regla).


¡No cambia la hora del sistema operativo para los ajustes de horario de verano! No es nada más que un cambio de zona horaria. El reloj del sistema siempre debe ser GMT. Y la hora del reloj de pared que le muestra al usuario se deriva de eso con el desplazamiento de zona horaria adecuado.


@Op. Ha implementado algo que se parece a una "espera agitada" y que siempre consumirá muchos recursos.

Estoy de acuerdo con los demás, no veo por qué necesita cambiar el reloj del sistema cuando va de verano a invierno.


Como han dicho otros, definitivamente no debería tener que cambiar el reloj del sistema. La marca de tiempo (milisegundos desde la época) es consistente en todas las computadoras en todo el mundo, pero la hora local depende de su ubicación, observación en el horario de verano y así sucesivamente. Por lo tanto, el problema es con la configuración regional del sistema operativo y la configuración de hora / fecha.

(Aún así, estoy de acuerdo en que si el reloj del sistema cambia, la JVM debería detectar esto y actualizar o despertar los hilos en espera para combatir el problema).


De acuerdo con el ticket de error, sus hilos no están congelados, se reanudarán una vez que el reloj alcance el lugar que tenía antes de que se modificara (por lo tanto, si se retrasaron una hora, sus hilos se reanudarán en 1 hora).

Por supuesto, eso todavía no es muy útil. La causa raíz parece ser que Thread.sleep() resuelve en una llamada del sistema que pone el hilo en suspensión hasta que se Thread.sleep() una marca de tiempo específica en el futuro, en lugar de una duración específica. Para Thread.sleep() implementar tu propia versión de Thread.sleep() que usa System.nanoTime() lugar de System.currentTimeMillis() o cualquier otra API dependiente del tiempo. Cómo hacerlo sin utilizar el Thread.sleep() no puedo decirlo, sin embargo.

Editar:

O bien, si crea una aplicación externa en otro idioma (como C o cualquier otra cosa que prefiera) que no haga otra cosa que esperar por una duración específica y luego salir. Luego, en lugar de llamar a Thread.sleep () en Java, puede generar una nueva instancia de este proceso externo, y luego llamar a waitFor () en él. Esto "suspenderá" el subproceso de Java para todos los propósitos prácticos, y mientras su aplicación externa pueda dormir durante el tiempo correcto, se reanudará en el momento correcto sin congelarse y sin dañar la CPU.

Parece un largo camino por recorrer para solucionar el problema, pero es la única solución viable que se me ocurre. Además, dado que generar un proceso externo es una operación relativamente costosa, probablemente funcione mejor si está durmiendo durante un tiempo relativamente largo (como varios cientos de ms o más). Para duraciones más cortas, simplemente podría continuar golpeando la CPU.


Por favor prueba el último jre 1.7.0_60. Resuelve el problema descrito causado por un cambio en el tiempo del sistema al pasado, al menos para sistemas con una versión glibc lanzada desde 2009.

El error relacionado http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6900441 se ha corregido y, por lo tanto, todas las funciones mencionadas por usted ( Thread.sleep, Object.wait, Thread.join, LockSupport.parkNanos, java.util.Timer and java.swing.Timer ) deberían funcionar como se espera.

Lo he probado con un kernel de linux 3.7.10.