Точность исполнения по расписанию 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()
Дрейф не существует.
Наконец, есть ли лучший способ отслеживать запланированное время выполнения при использовании запланированных исполнителей?