¿Es `System.currentTimeMillis ()` correcto en múltiples procesos?

Nov 23 2020

Tenemos una situación en la que un proceso maestro escribe en un registro.

Luego genera múltiples procesos de trabajo que escriben en sus propios registros. (Quería que los trabajadores se conectaran a través del maestro, pero hubo resistencia a esta idea por alguna razón).

Lo que quiero saber es, ¿puedo confiar en que las marcas de tiempo que terminan en varios archivos sean coherentes entre sí? es decir, si combino los archivos de registro en un solo archivo ordenado por instantes, ¿será verdadero el orden de los eventos? ¿En todos los sistemas operativos posibles?

La razón por la que pregunto esto es que tengo una situación extraña en la que parece que un proceso de trabajo ha registrado un error dos segundos después de que el maestro ha informado que el trabajador tuvo un error. Es como si el maestro pudiera ver el futuro. (Supongo que el maestro también es un señor del tiempo, pero eh ...)

Respuestas

13 BasilBourque Nov 23 2020 at 07:53

La llamada a System.currentTimeMillis, y su reemplazo moderno Instant.now, capturan el momento actual según lo informado por el sistema operativo host y el hardware del reloj de la computadora subyacente. El Javadoc y el código fuente prometen un reloj "basado en el mejor reloj del sistema disponible".

Entonces, no, no debería haber un salto hacia el futuro . Cada vez que llama a cualquiera de esos métodos, captura el momento actual.

Sin embargo, es posible que vea la ilusión de saltar hacia el futuro . Esto puede suceder por estas razones:

  • programación de hilos
  • reinicio del reloj
  • reloj falso

Programación de hilos

Esta ilusión puede ocurrir debido a lo que sucede después de que se captura el momento actual. La fracción de segundo después de capturar el momento actual, la ejecución de ese hilo puede pausarse. Algún otro hilo puede capturar un momento posterior, continuar informando ese momento. Finalmente, ese primer hilo se reanuda e informa su momento capturado anterior, pero tenga en cuenta cómo se informa más tarde de ese momento.

Tome este código de ejemplo.

package work.basil.example;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TellTime
{
    public static void main ( String[] args )
    {
        TellTime app = new TellTime();
        app.demo();
    }

    private void demo ( )
    {
        ExecutorService executorService = Executors.newCachedThreadPool();

        int countThreads = 15;
        List < Callable < Object > > tasks = new ArrayList <>( countThreads );
        for ( int i = 0 ; i < countThreads ; i++ )
        {
            Runnable tellTimeRunnable = ( ) -> System.out.println( Instant.now() );
            tasks.add( Executors.callable( tellTimeRunnable ) );
        }
        try
        {
            List < Future < Object > > list = executorService.invokeAll( tasks );
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }
    }
}

La primera vez que ejecuté ese código, encontré un salto en las dos últimas líneas de salida. La cuarta línea muestra un momento antes que la tercera. La quinta línea muestra un momento incluso antes.

2020-11-23T01:07:34.305318Z
2020-11-23T01:07:34.305569Z
2020-11-23T01:07:34.305770Z
2020-11-23T01:07:34.305746Z
2020-11-23T01:07:34.305434Z

En mi caso aquí, las llamadas a System.out.printlnse retrasaron en su ejecución, por lo que algunos momentos anteriores se informaron más tarde. Asimismo, sospecho que en su caso, el acto de registrar sus momentos capturados implicó varios retrasos, por lo que algunos momentos anteriores se registraron más tarde.

Reinicio del reloj

Como señala Stephen C en los comentarios a continuación , las computadoras a menudo están configuradas para ajustar automáticamente el reloj del hardware en función de la información de un servidor de tiempo. Los relojes de hardware de muchas computadoras son menos precisos de lo que imagina. Por lo tanto, es posible que el reloj de la computadora host se reinicie a una hora del día anterior o posterior para corregir la deriva del seguimiento del tiempo.

Tenga en cuenta que algunas computadoras restablecen su reloj a un punto de referencia de época como 1970-01-01 00: 00Z cuando se inician con una batería / condensador defectuoso o agotado que respalda el reloj del hardware. Ese momento de referencia de época se puede informar como el momento actual hasta que la computadora tenga la oportunidad de registrarse con el servidor de tiempo.

O algún ser humano podría ajustar manualmente la fecha y hora actual del reloj de la computadora. :-(

Su código puede capturar el momento actual en cualquier lado de este ajuste de reloj. Ahora puede parecer que un evento posterior ocurrió antes.

Reloj falso

En java.time , las llamadas como Instant.nowacceden a la Clockimplementación asignada actualmente . Por "asignado actualmente", me refiero al hecho de que en java.time , el Clockobjeto predeterminado se puede anular. Por lo general, eso sería solo para fines de prueba. Varios Clockobjetos pueden informar un momento fijo , un momento cambiado o pueden informar con una cadencia alterada .

Así que tenga en cuenta que una alternativa Clockpuede indicar deliberadamente una hora diferente, si su código de prueba especifica un Clockobjeto alternativo . Sin embargo, de forma predeterminada, siempre obtiene el momento actual en el momento en que se ejecuta la llamada al método.

Conclusión

Aquí hay una implicación importante: no se puede confiar completamente en el seguimiento del tiempo . Es posible que el momento actual se capture incorrectamente y que el informe de los momentos capturados esté desordenado.

Por lo tanto, cuando depure o investigue, mantenga siempre este pensamiento escondido en el fondo de su mente: las marcas de tiempo y su orden pueden no decirle toda la verdad. En última instancia, no puede saber con un 100% de certeza qué sucedió y cuándo.