11

Java のスケジュールされたエグゼキューターを使用しているときに遭遇した特異性があり、私が経験したことが正常であるかどうか疑問に思っていました。

定義済みの 5 秒間隔で実行されるタスクをスケジュールする必要があります。これらのタスクの実行には時々 5 秒以上かかることが予想されますが、実行時間が 5 秒未満になると、バックアップされたタスクのリストがすばやく連続して実行され、追いつく必要があります。タスクを実行するときは、最初にスケジュールされた実行時間が何であったかを知ることが重要です ( で考えscheduledExecutionTime()java.util.TimerTaskください)。最後に、予定された時間と実際の時間の差を追跡して、スケジュールがいつ、どの程度ずれているかを特定する必要があります。

これまでのところ、Java executor を使用してこれらすべてを実装してきました。次のクラスは、一般的な考え方を示しています。

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 エグゼキューターのドリフト

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

2 番目の質問は、ドリフトの量を減らす簡単な方法があるかどうかです。シンプルなjava.util.Timer、または単にを使用するThread.sleep()と、ドリフトは存在しません。

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

4

2 に答える 2

9

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

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

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

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 ミリ秒の約 10 回のジャンプが見られます。機械を使えばもっと見える。私が期待しているのは、大きな負のタイミングと正のタイミングです。

于 2011-12-07T13:09:40.937 に答える
0

私には正常に思えますが、5000/2 の範囲で変化します。format + println の代わりに高速ロギングを試す必要があるため、println のオーバーヘッドのドリフトは測定されません。面白いでしょう。

于 2011-12-07T13:10:25.553 に答える