¿Por qué el programador de Java presenta la deriva un tiempo considerable en Windows?

Frankie:

Tengo servicio en ejecución Java en Windows 7 que se ejecuta una vez al día en una SingleThreadScheduledExecutor. Nunca he dado mucho, aunque como es no crítica, pero recientemente se veía a los números y vio que el servicio se desplazaba a unos 15 minutos por día, que suena mucho a tal punto desenterrado.

Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
   long drift = (System.currentTimeMillis() - lastTimeStamp - seconds * 1000);
   lastTimeStamp = System.currentTimeMillis();
}, 0, 10, TimeUnit.SECONDS);

Este método se desvía bastante consistentemente +110mspor cada 10 segundos. Si me quedo en un intervalo de 1 segundo de los promedios de deriva +11ms.

Curiosamente, si hago lo mismo en un Timer()valores son bastante consistente con una desviación media de menos de un milisegundo completa.

new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
        long drift = (System.currentTimeMillis() - lastTimeStamp - seconds * 1000);
        lastTimeStamp = System.currentTimeMillis();
    }
}, 0, seconds * 1000);

Linux: no se mueve (ni con el Ejecutor, ni con temporizador)
Windows: derivas a lo loco con Ejecutor, no lo hace con el temporizador

Probado con Java8 y Java11.

Curiosamente, si se asume una deriva de 11 ms por segundo obtendrá 950400ms la deriva por día, que asciende a 15.84 minutespor día. Así que es bastante consistente.

La pregunta es: ¿por qué?
¿Por qué esto suceda con un SingleThreadExecutor pero no con un temporizador.

Actualización 1: comentario siguiente de repollo que intentó en el hardware de múltiples diferente. Lo que encontré es que este problema no se manifiesta en cualquier hardware personal. Sólo en la empresa en uno. En el hardware de la compañía también se manifiesta en Win10, aunque un orden de magnitud menor.

Michael Berry:

Como a cabo en punta en los comentarios, la ScheduledThreadPoolExecutorbasa sus cálculos en System.nanoTime(). Para mejor o peor, la vieja TimerAPI embargo precedido nanoTime(), por lo que utiliza System.currentTimeMillis()en su lugar.

La diferencia aquí puede parecer sutil, pero es más significativo de lo que cabría esperar. Contrariamente a la creencia popular, nanoTime()es no sólo una "versión más precisa" de currentTimeMillis(). Millis está bloqueado a la hora del sistema, mientras que los nanos no lo es. O como los documentos ponen :

Este método sólo se puede utilizar para medir el tiempo transcurrido y no se relaciona con cualquier otra noción de sistema o de pared reloj de tiempo. [...] Los valores devueltos por este método se vuelven significativo sólo cuando la diferencia entre dos de tales valores, obtenidos dentro de la misma instancia de una máquina virtual Java, se calcula.

En su ejemplo, usted no está siguiendo esta guía para los valores sean "significativa" - como es comprensible, ya que la ScheduledThreadPoolExecutorúnica utiliza nanoTime()como un detalle de implementación. Pero el resultado final es el mismo, que es que no se puede garantizar que esto permanezca sincronizado con el reloj del sistema.

¿Pero por qué no? Los segundos son segundos, a la derecha, por lo que los dos deben estar sincronizado a partir de cierto punto conocido?

Pues bien, en teoría, sí. Pero en la práctica, probablemente no.

Echando un vistazo a código nativo relevante en las ventanas :

LARGE_INTEGER current_count;
QueryPerformanceCounter(&current_count);
double current = as_long(current_count);
double freq = performance_frequency;
jlong time = (jlong)((current/freq) * NANOSECS_PER_SEC);
return time;

Vemos nanos()utiliza la QueryPerformanceCounterAPI, que trabaja por QueryPerformanceCounterconseguir los "tics" de una frecuencia que está definida por QueryPerformanceFrequency. Esa frecuencia permanecerá idéntica, pero el temporizador se basa apagado, y su algoritmo synchronistaion que utiliza Windows, varía según la configuración, el sistema operativo y el hardware subyacente. Incluso haciendo caso omiso de lo anterior, es que nunca va a estar cerca del 100% exacto (que se basa de un razonablemente barato oscilador de cristal en algún lugar en el tablero, no un estándar de tiempo de cesio!) Así que va a la deriva con el tiempo del sistema como NTP mantiene en sincronía con la realidad.

En particular, este enlace da antecedentes de utilidad, y refuerza lo anterior Pont:

Cuando necesite marcas de tiempo con una resolución de 1 microsegundo o mejor y no necesita las marcas de tiempo para sincronizarse a una referencia de tiempo externa , elija QueryPerformanceCounter.

(Negrita es mía.)

Para su caso específico de Windows 7 deficiente, una nota que en Windows 8 +, el algoritmo de sincronización TSC se mejoró, y QueryPerformanceCounterfue siempre basado en una TSC (como se oponen a Windows 7, en el que podría ser un TSC, HPET o la ACPI PM temporizador - el último de los cuales es especialmente poco preciso) sospecho que esta es la razón más probable es que la situación mejore enormemente en Windows 10..

Una vez dicho esto, los factores anteriores significan que todavía no se puede confiar en la ScheduledThreadPoolExecutorde mantener en el tiempo con el tiempo "real" - que siempre va a la deriva. Si la deriva que es un problema, entonces no es una solución que puede confiar en este contexto.

Nota al margen: En Windows 8 +, hay una GetSystemTimePreciseAsFileTimefunción que ofrece la alta resolución de QueryPerformanceCountercombinado con la precisión de la hora del sistema. Si Windows 7 se dejó caer como una plataforma de apoyo, esto podría, en teoría, ser utilizado para proporcionar un System.getCurrentTimeNanos()método o similares, asumiendo que existen otras funciones nativas similares para otras plataformas soportadas.

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=68527&siteId=1
Recomendado
Clasificación