`System.currentTimeMillis ()` est-il correct sur plusieurs processus?
Nous avons une situation où un processus maître écrit dans un journal.
Il génère ensuite plusieurs processus de travail qui écrivent dans leurs propres journaux. (Je voulais que les travailleurs se connectent via le maître, mais il y avait une résistance à cette idée pour une raison quelconque.)
Ce que je veux savoir, c'est, puis-je être sûr que les horodatages qui se retrouvent dans les fichiers multiples sont cohérents les uns avec les autres? c'est-à-dire que si je fusionne les fichiers journaux en un seul fichier trié par instant, l'ordre des événements sera-t-il vrai? Sur tous les systèmes d'exploitation possibles?
La raison pour laquelle je pose cette question est que j'ai une situation étrange où il semble qu'un processus de travail a enregistré une erreur deux secondes après que le maître a signalé que le travailleur avait une erreur. C'est comme si le maître était capable de voir l'avenir. (Je suppose que le maître est aussi un seigneur du temps, mais euh ...)
Réponses
L'appel à System.currentTimeMillis, et son remplacement moderne Instant.now
, capturent le moment actuel tel que rapporté par le système d'exploitation hôte et le matériel d'horloge de l'ordinateur sous-jacent. Le Javadoc et le code source promettent une horloge «basée sur la meilleure horloge système disponible».
Donc, non, il ne devrait pas y avoir de saut dans le futur . Chaque fois que vous appelez l'une de ces méthodes, vous capturez le moment actuel.
Cependant, vous pouvez voir l'illusion de sauter dans le futur . Cela peut se produire pour ces raisons:
- planification des threads
- réinitialisation de l'horloge
- fausse horloge
Planification des threads
Cette illusion peut se produire à cause de ce qui se passe après la capture du moment actuel. La fraction de seconde après la capture du moment actuel, l'exécution de ce thread peut être interrompue. Un autre fil peut alors capturer un moment ultérieur, continuer à signaler ce moment. Finalement, ce premier fil reprend et rapporte son moment capturé antérieur - mais notez comment le rapport de ce moment se produit plus tard.
Prenons cet exemple de code.
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 toute première fois que j'ai exécuté ce code, j'ai trouvé un tel saut dans les deux dernières lignes de sortie. La 4ème ligne montre un moment avant la 3ème. La 5ème ligne montre un moment encore plus tôt.
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
Dans mon cas ici, les appels à ont System.out.println
été retardés dans leur exécution, donc certains moments antérieurs ont été rapportés plus tard. De même, je soupçonne que dans votre cas, le fait de consigner vos moments capturés a impliqué divers retards, de sorte que certains moments antérieurs ont été enregistrés plus tard.
Réinitialisation de l'horloge
Comme Stephen C le souligne dans les commentaires ci - dessous , les ordinateurs sont souvent configurés pour ajuster automatiquement l'horloge matérielle en fonction des informations d'un serveur de temps. Les horloges matérielles de nombreux ordinateurs sont moins précises que vous ne l'imaginez. Ainsi, l'horloge de l'ordinateur hôte peut être réinitialisée à une heure antérieure ou ultérieure pour corriger la dérive de suivi du temps.
Sachez que certains ordinateurs réinitialisent leur horloge à un point de référence d'époque tel que 1970-01-01 00: 00Z lorsqu'ils sont démarrés avec une batterie / condensateur défectueux ou épuisé soutenant l'horloge matérielle. Ce moment de référence d'époque peut être signalé comme le moment actuel jusqu'à ce que l'ordinateur ait la possibilité de s'enregistrer auprès du serveur de temps.
Ou un humain pourrait ajuster manuellement la date et l'heure actuelles de l'horloge de l'ordinateur. :-(
Votre code peut capturer le moment actuel de chaque côté de ce réglage d'horloge. Maintenant, un événement ultérieur peut sembler s'être produit plus tôt.
Fausse horloge
Dans java.time , des appels tels que l' Instant.now
accès à l' Clockimplémentation actuellement attribuée . Par «actuellement assigné», je fais référence au fait que dans java.time , l' Clock
objet par défaut peut être remplacé. Habituellement, ce serait uniquement à des fins de test. Divers Clock
objets peuvent signaler un moment fixe , un moment décalé ou peuvent rapporter avec une cadence modifiée .
Sachez donc qu'un autre Clock
peut volontairement indiquer une heure différente, si votre code de test spécifie un Clock
objet alternatif . Par défaut, vous obtenez toujours le moment actuel au moment où l'appel de méthode s'exécute.
Conclusion
Il y a une implication majeure ici: le suivi du temps ne peut pas être entièrement fiable . Le moment actuel peut être capturé de manière incorrecte et le signalement des moments capturés peut être dans le désordre.
Ainsi, lors du débogage ou de l'enquête, gardez toujours cette pensée cachée au fond de votre esprit: les horodatages et leur ordre peuvent ne pas vous dire toute la vérité. Vous ne pouvez finalement pas savoir avec 100% de certitude ce qui s'est passé quand.