2011-12-07 11 views
9

Java実行予定実行プログラムを使用しているときに遭遇した特有の問題があり、私が経験したことが正常かどうか疑問に思っていました。Java実行予定者の精度

私は、定義済みの5秒間に実行されるタスクをスケジュールする必要があります。これらのタスクの実行には5秒以上かかることが予想されますが、実行する時間が5秒を下回ると、バックアップされたタスクのリストはすばやく連続して実行される必要があります。タスクを実行するときは、元の実行予定時刻が何であるかを知ることが重要です(scheduledExecutionTime()java.util.TimerTaskと考える)。最後に、予定と実際の時間の違いを追跡して、スケジュールが「漂流」している時期とその程度を特定する必要があります。

これまでのところ私は、Java実行プログラムを使用してこのすべてを実装している、と以下のクラスは、一般的な考え方を示しています。上記のコードを実行する

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

は、理想的に近いべきであるドリフト(することを示しています0を可能な限り)は数秒間変動します。その結果、早すぎたり遅く実行されたタスクが発生します。私は約150分のためにこれを実行した結果からグラフを作成しました:

Java executor drift

をだから私の最初の質問は、これが正常であるかどうかです。私の環境は、32ビットのWindows XPとJava 1.5 update 21(ただし、Java 6 update 22では同様の結果が得られます)で構成されています。

第2の問題は、ドリフトの量を減らす簡単な方法があるかどうかです。私が単純なjava.util.Timer、あるいはちょうどThread.sleep()を使用すると、ドリフトは存在しません。

最後に、スケジュールされたエグゼキュータを使用しているときに、スケジュールされた実行時間を追跡する優れた方法はありますか?

答えて

8

スケジュールされたエグゼキュータサービスは、currentTimeMillisのようにドリフトしないSystem.nanoTimeを使用します。複数のCPUソケットを備えたXPシステムで実行している場合を除きます。 OSコールSystem.nanoTime()がソケット間で一貫していないため、ソケットが実行中のソケットを切り替えるXPにバグがあります。 (これはVista/7では問題ありません)

1つのソケットを持つLinuxシステムでは、プログラムは0〜3 msのドリフトを報告します。

このプログラムをお試しください。

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

i7システムでは、約2 msの約10回のジャンプがあります。私はマシンを使用する場合、私はより多くを参照してください。私が期待しているのは、大きな負のタイミングと正のタイミングです。

0

私には普通のようですが、それは5000/2の範囲で異なります。 format + printlnではなく、高速ロギングを試してください。そうすれば、printlnのオーバーヘッドドリフトは測定されません。面白いだろうか。