Есть особенность, с которой я столкнулся при использовании запланированных исполнителей 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 21 (хотя обновление Java 6 22 дает аналогичные результаты).
Второй вопрос заключается в том, есть ли простой способ уменьшить величину дрейфа. Если я использую простую java.util.Timer
или даже просто Thread.sleep()
, дрейфа не существует.
Наконец, есть ли лучший способ отслеживания запланированного времени выполнения при использовании запланированных исполнителей?