Точность исполнения по расписанию Java - PullRequest
9 голосов
/ 07 декабря 2011

Есть одна особенность, с которой я столкнулся при использовании запланированных исполнителей 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 дает аналогичные результаты).

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

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

Ответы [ 2 ]

8 голосов
/ 07 декабря 2011

Запланированная служба исполнителя использует System.nanoTime, который не дрейфует так сильно, как currentTimeMillis.За исключением случаев, когда вы работаете в системе XP с более чем одним сокетом процессора.В XP есть ошибка, из-за которой системный вызов System.nanoTime () не согласован между сокетами, поэтому, поскольку поток переключает, на каком сокете он работает, вы можете ожидать, что это произойдет.(Это не проблема в Vista / 7)

В системе 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 я вижу около 10 скачков до 2 мс.Если я использую машину, я вижу больше.То, что вы ожидаете увидеть, это большое негативное и позитивное время.

0 голосов
/ 07 декабря 2011

Мне кажется нормальным, оно колеблется в диапазоне 5000/2. Вместо формата + println вы должны попробовать быструю регистрацию, так что дрейф накладных println не измеряется. Было бы интересно.

...