2011-12-07 13 views
9

Esiste una particolarità che ho riscontrato durante l'utilizzo di esecutori pianificati Java e mi chiedevo se ciò che provavo fosse normale.Precisione esecutore pianificato Java

Devo pianificare le attività che vengono eseguite a una velocità predefinita di 5 secondi. Si prevede che queste attività richiederanno di volta in volta più di 5 secondi, ma quando il tempo necessario per eseguirle scende sotto i 5 secondi, l'elenco di backup delle attività dovrebbe essere eseguito in rapida successione per recuperare il ritardo. Quando si eseguono le attività, è importante sapere qual è il tempo di esecuzione pianificato originale (si pensi a scheduledExecutionTime() in java.util.TimerTask). Infine, ho bisogno di tracciare la differenza tra il tempo programmato e il tempo reale per identificare quando il programma è "alla deriva" e di quanto.

Finora ho realizzato tutto questo utilizzando esecutori Java, e la seguente classe illustra l'idea generale:

public class ExecutorTest { 
    public static final long PERIOD = 5000; 

    public static void main(String[] args) { 
     Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
       new Command(), 0, PERIOD, TimeUnit.MILLISECONDS); 
    } 

    private static final class Command implements Runnable { 
     long timestamp = 0; 

     public void run() { 
      long now = System.currentTimeMillis(); 

      if (timestamp == 0) { 
       timestamp = now; 
      } 

      // Drift is the difference between scheduled time and execution time 
      long drift = now - timestamp; 

      String format = "Ran at %1$tF %<tT,%<tL; drift: %2$dms"; 
      System.out.println(String.format(format, now, drift)); 

      timestamp += PERIOD; 
     } 
    } 
} 

L'esecuzione del codice di cui sopra dimostra che la deriva (che idealmente dovrebbe essere il più vicino a 0 possibile) fluttua di pochi secondi, il cui risultato è che i compiti vengono eseguiti prematuramente o in ritardo. Ho creato un grafico dai risultati di funzionamento di questo per circa 150 minuti:

Java executor drift

Quindi la mia prima domanda è se questo è normale. Il mio ambiente è costituito da Windows XP a 32 bit e aggiornamento 21 di Java 1.5 (sebbene l'aggiornamento 22 di Java 6 produca risultati simili).

La seconda domanda è se esiste un modo semplice per ridurre la quantità di deriva. Se utilizzo un semplice java.util.Timer o anche solo Thread.sleep(), la deriva è inesistente.

Infine, c'è un modo migliore per tenere traccia del tempo di esecuzione pianificato quando si usano gli esecutori programmati?

risposta

8

Il servizio di esecuzione pianificata utilizza System.nanoTime che non deriva come CurrentTimeMillis. Tranne se stai utilizzando un sistema XP con più di un socket per CPU. C'è un bug in XP in cui l'uso del sistema operativo System.nanoTime() non è coerente tra i socket, così come il thread cambia su quale socket è in esecuzione, puoi aspettarti di vederlo saltare. (Questo non è un problema su Vista/7)

Su un sistema Linux con una presa il programma riporta una deroga di 0 - 3 ms.

Prova questo programma.

public static void main(String... args) throws Exception { 
    long start = System.nanoTime(); 
    long time = start; 
    while(time < start + 3e10) { 
     long now = System.nanoTime(); 
     if (now < time || now > time + 50000) { 
      System.out.println(now - time); 
      now = System.nanoTime(); 
     } 
     time = now; 
    } 
} 

Su un sistema i7 vedo circa 10 salti fino a 2 ms. Se uso la macchina, vedo di più. Quello che mi aspetto potresti vedere sono i tempi negativi e positivi.

0

Mi sembra normale, varia nell'intervallo di 5000/2. Invece di format + println dovresti provare una registrazione veloce, quindi la deriva generale di println non viene misurata. Sarebbe interessante.