2016-01-13 8 views
21

L'impostazione predefinita java.time.Clock è basata su System.currentTimeMillis(). Come discusso qui ad esempio, Monotonically increasing time in Java?, non è garantito che sia monotonico.Esiste un'implementazione di clock coerente (monotona) in Java?

E in effetti, ho regolarmente una situazione in cui l'ora del sistema viene regolata automaticamente pochi secondi al passato e anche l'orologio java salta indietro.

//now() returns 2016-01-13T22:34:05.681Z 
order.setCreationTime(Instant.now()); 

//... something happens, order gets cancelled 

//now() returns 2016-01-13T22:34:03.123Z 
//which is a few seconds before the former one, 
//even though the call was performed later - in any reasonable sense. 
//The recorded history of events is obviously inconsistent with the real world. 
order.setCancelationTime(Instant.now()); 

E 'quindi impossibile effettuare cose time-sensitive, come la storia di registrazione e analisi caso, quando non si può fare affidamento su tempo andando in una sola direzione.

Il post di cui sopra dice che System.nanoTime() è monotono (se il sistema sottostante lo supporta). Quindi, se voglio basare il mio codice sull'API java.time, mi servirebbe Clock che usi nanoTime internamente per garantire un flusso unidirezionale del tempo. Forse qualcosa del genere funzionerebbe. O no?

public class MyClock() extends java.time.Clock { 

    private final long adjustment; 

    public MyClock() { 
     long cpuTimeMillis = System.nanoTime()/1000000; 
     long systemTimeMillis = System.currentTimeMillis(); 
     adjustment = systemTimeMillis - cpuTimeMillis; 
    } 

    @Override 
    public long millis() { 
     long currentCpuTimeMillis = System.nanoTime()/1000000; 
     return currentCpuTimeMillis + adjustment; 
    } 
} 

È solo uno schizzo, non un'implementazione completa di Clock. E suppongo che una corretta implementazione dovrebbe anche eseguire la regolazione su un altro Clock passato nel costruttore, piuttosto che direttamente contro il currentTimeMillis().

Oppure, è già disponibile un'implementazione dell'orologio monotona? Direi che ci devono essere state molte persone con lo stesso problema.

Conclusione

Grazie per i commenti ispiratori e risposte. Ci sono diversi punti interessanti sparsi nei commenti, quindi lo riassumerò qui.

1. Monotonic orologio

Per quanto riguarda la mia domanda iniziale, sì, è possibile avere l'orologio monotona che non è influenzata dal tempo del sistema saltando all'indietro. Tale implementazione può essere basata su System.nanoTime() come suggerito sopra. In passato c'erano problemi con questo approccio, ma dovrebbe funzionare bene sui moderni sistemi odierni. Questo approccio è già implementata, ad esempio nella biblioteca Time4J, il loro orologio monotona può essere facilmente convertito in java.time.Clock:

Clock clock = TemporalType.CLOCK.from(SystemClock.MONOTONIC); 

2. tempo adeguato sistema di controllo

E 'possibile configurare gestione del tempo di sistema (ntpd in unix/linux), in modo che il tempo di sistema non si arresti praticamente mai (si rallenta se necessario), quindi si può fare affidamento sul fatto che il tempo di sistema è monotonico e non è necessaria la magia dell'orologio in Java .

Vado in questo modo, poiché la mia app è lato server e posso avere il tempo sotto controllo. In realtà, ho sperimentato le anomalie in un ambiente sperimentale che ho installato io stesso (solo con conoscenza superficiale del dominio) e utilizzavo solo il ntpdate client (che può saltare all'indietro se il tempo non è sincronizzato), piuttosto che ntpd (che può essere configurato in modo che non salti indietro).

3. Utilizzando sequenze piuttosto che l'orologio

Quando si ha la necessità di tenere traccia di una forte relazione quello che è successo prima cosa, è più sicuro di dare gli eventi numeri di serie da una sequenza atomicamente generato e non fare affidamento sulla parete orologio. Diventa l'unica opzione una volta che l'applicazione è in esecuzione su diversi nodi (che però non è il mio caso).

+3

Anche se il tempo è una cosa monotona, le date non lo sono. Le date sono una costruzione umana per rappresentare il tempo. Se hai bisogno di qualcosa per rappresentare il tempo puramente, forse potresti usare una sequenza di database o qualsiasi altra informazione affidabile. – Leo

+1

nanoTime potrebbe non fare ciò che ti serve. http://www.principiaprogramatica.com/?p=16 – Bill

+1

Potresti essere in grado di studiare la fonte della mia [implementazione dell'orologio monotonico] (http://time4j.net/javadoc-en/net/time4j/SystemClock.html #MONOTONIC) per trasferire alcune idee sul tuo orologio basato su java.time. Fonte: vedi: https://github.com/MenoData/Time4J/blob/master/core/src/main/java/net/time4j/SystemClock.java –

risposta

3

Come dice @ the8472, la chiave è di avere la sincronizzazione dell'ora sulla macchina (dove jvm gira) corretta.

Se si programma un client, può davvero essere pericoloso affidarsi agli orologi di sistema. Ma per i server esiste una soluzione: si potrebbe prendere in considerazione l'utilizzo di NTP con una configurazione rigorosa.

In here spiegano in modo semplice che NTP rallenta il tempo e non lo riporta indietro.

E this NTP documentation dice:

A volte, in particolare quando ntpd primo avvio, l'errore potrebbe superare i 128 ms. Ciò può occasionalmente causare l'inversione dell'orologio all'indietro se l'ora locale è superiore a 128 s nel futuro relativo al server. In alcune applicazioni, questo comportamento potrebbe essere inaccettabile. Se l'opzione -x è inclusa nella riga di comando, l'orologio non verrà mai scalato e verranno utilizzate solo correzioni di rotazione.

3

Si noti che il nanoTime può aumentare monotonicamente ma non si riferisce in modo corretto al tempo di parete, ad es. a causa di eventi di ibernazione, sospensione VM e cose simili.

E se si inizia a distribuire le cose su più server, la sincronizzazione su currentMillis potrebbe anche provocare un nuovo morso.

Forse dovresti prendere in considerazione il controllo del tempo di sistema dei tuoi server.

Oppure traccia la sequenza relativa degli eventi separatamente dal momento in cui sono stati presumibilmente registrati.