`System.currentTimeMillis ()` está correto em vários processos?

Nov 23 2020

Temos uma situação em que um processo mestre grava em um log.

Em seguida, ele gera vários processos de trabalho que gravam em seus próprios logs. (Eu queria que os trabalhadores registrassem através do mestre, mas houve resistência a essa ideia por algum motivo.)

O que eu quero saber é, posso confiar que os carimbos de data / hora que terminam nos vários arquivos são consistentes entre si? ou seja, se eu mesclar os arquivos de log em um único arquivo, classificando por instante, a ordem dos eventos será verdadeira? Em todos os sistemas operacionais possíveis?

Estou perguntando isso porque tenho uma situação estranha em que parece que um processo de trabalho registrou um erro dois segundos depois que o mestre relatou que o trabalhador teve um erro. É como se o mestre fosse capaz de ver o futuro. (Eu acho que o mestre também é um senhor do tempo, mas uh ...)

Respostas

13 BasilBourque Nov 23 2020 at 07:53

A chamada para System.currentTimeMillis, e sua substituição moderna Instant.now, capturam o momento atual conforme relatado pelo sistema operacional host e pelo hardware de relógio do computador subjacente. O Javadoc e o código-fonte prometem um relógio “baseado no melhor relógio de sistema disponível”.

Portanto, não, não deve haver saltos para o futuro . Cada vez que você chama um desses métodos, você captura o momento atual.

No entanto, você pode ver a ilusão de pular para o futuro . Isso pode acontecer por estes motivos:

  • programação de discussão
  • acertando o relógio
  • relógio falso

Agendamento de discussão

Essa ilusão pode ocorrer por causa do que acontece depois que o momento atual é capturado. A fração de segundo depois de capturar o momento atual, a execução desse thread pode ser pausada. Algum outro tópico pode capturar um momento posterior, continue relatando aquele momento. Eventualmente, aquele primeiro thread é retomado e relata seu momento capturado anteriormente - mas observe como o relato daquele momento acontece mais tarde.

Pegue este código de exemplo.

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();
        }
    }
}

A primeira vez que executei esse código, encontrei um salto nas duas últimas linhas de saída. A 4ª linha mostra um momento antes da 3ª. A 5ª linha mostra um momento ainda 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

No meu caso aqui, as chamadas para System.out.printlnatrasaram sua execução, então alguns momentos anteriores foram relatados posteriormente. Da mesma forma, suspeito que, no seu caso, o ato de registrar seus momentos capturados envolveu vários atrasos, de modo que alguns momentos anteriores foram registrados posteriormente.

Acertando o relógio

Como Stephen C aponta nos comentários abaixo , os computadores são frequentemente configurados para ajustar automaticamente o relógio do hardware com base nas informações de um servidor de horário. Os relógios de hardware em muitos computadores são menos precisos do que você pode imaginar. Portanto, o relógio do computador host pode muito bem ser redefinido para uma hora do dia anterior ou posterior para corrigir o desvio do controle de tempo.

Esteja ciente de que alguns computadores reconfiguram seu relógio para um ponto de referência de época , como 1970-01-01 00: 00Z, quando inicializado com uma bateria / capacitor defeituoso ou esgotado, acompanhando o relógio do hardware. Esse momento de referência de época pode ser relatado como o momento atual até que o computador tenha a chance de verificar com o servidor de horário.

Ou algum ser humano poderia ajustar manualmente a data e hora atuais do relógio do computador. :-(

Seu código pode capturar o momento atual em qualquer um dos lados desse ajuste do relógio. Agora, um evento posterior pode parecer ter ocorrido antes.

Relógio falso

Em java.time , chamadas como Instant.nowacessam a Clockimplementação atualmente atribuída . Por "atualmente atribuído", refiro-me ao fato de que em java.time , o Clockobjeto padrão pode ser substituído. Normalmente, isso seria apenas para fins de teste. Vários Clockobjetos podem relatar um momento fixo , um momento alterado ou podem relatar com uma cadência alterada .

Portanto, esteja ciente de que um alternativo Clockpode propositalmente indicar um horário diferente, se seu código de teste especificou um Clockobjeto alternativo . Por padrão, você sempre obtém o momento atual no momento em que a chamada do método é executada.

Conclusão

Há uma implicação importante aqui: o controle do tempo não pode ser totalmente confiável . O momento atual pode ser capturado incorretamente e o relato dos momentos capturados pode estar fora de ordem.

Portanto, ao depurar ou investigar, sempre mantenha este pensamento escondido no fundo da sua mente: os carimbos de data / hora e sua ordem podem não estar dizendo toda a verdade. Em última análise, você não pode saber com 100% de certeza o que aconteceu e quando.