Ist "System.currentTimeMillis ()" über mehrere Prozesse hinweg korrekt?
Wir haben eine Situation, in der ein Master-Prozess in ein Protokoll schreibt.
Anschließend werden mehrere Worker-Prozesse erzeugt, die in ihre eigenen Protokolle schreiben. (Ich wollte, dass sich die Arbeiter beim Meister anmelden, aber aus irgendeinem Grund gab es Widerstand gegen diese Idee.)
Was ich wissen möchte ist, kann ich darauf vertrauen, dass die Zeitstempel, die in den mehreren Dateien landen, miteinander übereinstimmen? Wenn ich also die Protokolldateien zu einer einzelnen Datei zusammenführe, die nach Augenblicken sortiert ist, ist die Reihenfolge der Ereignisse dann wahr? Über alle möglichen Betriebssysteme hinweg?
Der Grund, warum ich dies frage, ist, dass ich eine seltsame Situation habe, in der es so aussieht, als hätte ein Arbeitsprozess zwei Sekunden, nachdem der Master gemeldet hat, dass der Arbeiter einen Fehler hatte, einen Fehler protokolliert. Es ist, als könnte der Meister in die Zukunft sehen. (Ich denke, der Meister ist auch ein Zeitherr, aber äh ...)
Antworten
Der Aufruf von System.currentTimeMillisund sein moderner Ersatz Instant.now
erfassen beide den aktuellen Moment, der vom Host-Betriebssystem und der zugrunde liegenden Computeruhr-Hardware gemeldet wird. Der Javadoc und der Quellcode versprechen eine Uhr „basierend auf der besten verfügbaren Systemuhr“.
Also, nein, es sollte keinen Sprung in die Zukunft geben . Jedes Mal, wenn Sie eine dieser Methoden aufrufen, erfassen Sie den aktuellen Moment.
Möglicherweise sehen Sie jedoch die Illusion, in die Zukunft zu springen . Dies kann aus folgenden Gründen geschehen:
- Thread-Planung
- Uhr zurücksetzen
- gefälschte Uhr
Thread-Planung
Diese Illusion kann aufgrund dessen auftreten, was passiert, nachdem der aktuelle Moment erfasst wurde. In Sekundenbruchteilen nach der Erfassung des aktuellen Moments kann die Ausführung dieses Threads angehalten werden. Ein anderer Thread kann dann einen späteren Moment erfassen und diesen Moment weiterhin melden. Schließlich wird dieser erste Thread fortgesetzt und der zuvor erfasste Moment gemeldet. Beachten Sie jedoch, wie die Berichterstattung über diesen Moment später erfolgt.
Nehmen Sie diesen Beispielcode.
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();
}
}
}
Als ich diesen Code zum ersten Mal ausführte, fand ich einen solchen Sprung in den letzten beiden Ausgabezeilen. Die 4. Zeile zeigt einen Moment früher als die 3 .. Die 5. Zeile zeigt einen Moment noch früher.
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
In meinem Fall hier haben sich die Aufrufe System.out.println
in ihrer Ausführung verzögert, so dass einige frühere Momente später gemeldet wurden. Ebenso vermute ich, dass in Ihrem Fall das Aufzeichnen Ihrer erfassten Momente verschiedene Verzögerungen mit sich brachte, so dass einige frühere Momente später protokolliert wurden.
Zurücksetzen der Uhr
Wie Stephen C in den Kommentaren unten ausführt , sind Computer häufig so konfiguriert, dass die Hardware-Uhr basierend auf Informationen von einem Zeitserver automatisch angepasst wird. Die Hardware-Uhren in vielen Computern sind weniger genau als Sie sich vorstellen können. Daher kann die Uhr des Host-Computers möglicherweise auf eine frühere oder spätere Tageszeit zurückgesetzt werden, um die Zeitverfolgungsdrift zu korrigieren.
Beachten Sie, dass einige Computer ihre Uhr zurück auf einen Rücksetz Epoche Referenzpunkt, wie 1970-01-01 00: 00Z , wenn sie mit einem fehlerhaften oder verbrauchte Batterie / Kondensator Sichern der Hardware - Uhr gestartet. Dieser Epochenreferenzmoment kann als aktueller Moment gemeldet werden, bis der Computer die Möglichkeit erhält, sich beim Zeitserver anzumelden.
Oder ein Mensch könnte das aktuelle Datum und die Uhrzeit der Computeruhr manuell einstellen. :-(
Ihr Code kann den aktuellen Moment auf beiden Seiten dieser Uhreinstellung erfassen. Jetzt scheint ein späteres Ereignis früher aufgetreten zu sein.
Gefälschte Uhr
Aufrufe in java.time greifen beispielsweise Instant.now
auf die aktuell zugewiesene ClockImplementierung zu. Mit "aktuell zugewiesen" beziehe ich mich auf die Tatsache, dass in java.time das Standardobjekt Clock
überschrieben werden kann. Normalerweise dient dies nur zu Testzwecken. Verschiedene Clock
Objekte können einen festen Moment , einen verschobenen Moment oder eine veränderte Trittfrequenz melden .
Beachten Sie also, dass eine Alternative Clock
absichtlich eine andere Zeit angeben kann, wenn Ihr Testcode ein alternatives Clock
Objekt angegeben hat. Standardmäßig erhalten Sie jedoch immer den aktuellen Zeitpunkt zum Zeitpunkt der Ausführung des Methodenaufrufs.
Fazit
Hier gibt es eine wichtige Auswirkung: Die Zeiterfassung kann nicht vollständig als vertrauenswürdig eingestuft werden . Der aktuelle Moment wird möglicherweise falsch erfasst , und die Meldung der erfassten Momente ist möglicherweise nicht in Ordnung.
Behalten Sie diesen Gedanken beim Debuggen oder Nachforschen immer im Hinterkopf: Die Zeitstempel und ihre Reihenfolge sagen möglicherweise nicht die ganze Wahrheit. Sie können letztendlich nicht mit 100% iger Sicherheit wissen, was wann passiert ist.