Точность исполнения по расписанию 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 минут:

Итак, мой первый вопрос: нормально ли это? Моя среда состоит из 32-битной Windows XP и Java 1.5 update 21 (хотя Java 6 update 22 дает аналогичные результаты).

Второй вопрос: есть ли простой способ уменьшить количество дрейфа. Если я использую простойjava.util.Timer или даже простоThread.sleep()Дрейф не существует.

Наконец, есть ли лучший способ отслеживать запланированное время выполнения при использовании запланированных исполнителей?

Ответы на вопрос(2)

Ваш ответ на вопрос