В диагностических целях я хочу иметь возможность обнаруживать изменения в системных часах времени в долговременном серверном приложении. Поскольку System.currentTimeMillis()
основан на времени настенных часов, а System.nanoTime()
основан на системном таймере, который является независимым (*) времени настенных часов, я думал, что могу использовать изменения в разнице между этими значениями для обнаружения изменений системного времени.
Я написал приложение для быстрого тестирования, чтобы увидеть, насколько стабильна разница между этими значениями, и, к моему удивлению, значения сразу расходятся для меня на уровне нескольких миллисекунд в секунду. Несколько раз я видел гораздо более быстрые расхождения. Это на 64-разрядном рабочем столе Win7 с Java 6. Я не пробовал эту тестовую программу под Linux (или Solaris или MacOS), чтобы увидеть, как она работает. Для некоторых запусков этого приложения расхождение положительное, для некоторых работает отрицательно. Похоже, что это зависит от того, что делает рабочий стол, но это трудно сказать.
public class TimeTest {
private static final int ONE_MILLION = 1000000;
private static final int HALF_MILLION = 499999;
public static void main(String[] args) {
long start = System.nanoTime();
long base = System.currentTimeMillis() - (start / ONE_MILLION);
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Don't care if we're interrupted
}
long now = System.nanoTime();
long drift = System.currentTimeMillis() - (now / ONE_MILLION) - base;
long interval = (now - start + HALF_MILLION) / ONE_MILLION;
System.out.println("Clock drift " + drift + " ms after " + interval
+ " ms = " + (drift * 1000 / interval) + " ms/s");
}
}
}
Неточности с временем Thread.sleep()
, а также прерывания, должны быть совершенно неактуальны для дрейфа таймера.
Оба этих вызова Java "System" предназначены для использования в качестве измерения: один для измерения различий в настенных часах, а другой для измерения абсолютных интервалов, поэтому, когда часы реального времени не изменяются, эти значения должны меняться очень близко к одной и той же скорости, верно? Является ли это ошибкой или слабостью или неудачей в Java? Есть ли что-то в ОС или аппаратных средствах, которые мешают Java быть более точными?
Я полностью ожидаю некоторого дрейфа и джиттера (**) между этими независимыми измерениями, но я ожидал гораздо меньше минуты в день дрейфа. 1 мсек в секунду дрейфа, если монотонно, составляет почти 90 секунд! Мой худший случай наблюдал дрейф, возможно, в десять раз. Каждый раз, когда я запускаю эту программу, я вижу дрейф при первом измерении. До сих пор я не запускал программу более 30 минут.
Я ожидаю увидеть небольшую случайность в напечатанных значениях из-за дрожания, но почти во всех прогонах программы я вижу постоянное увеличение разницы, часто до 3 мсек в секунду увеличения и в несколько раз больше более того.
Есть ли в какой-либо версии Windows механизм, подобный Linux, который настраивает тактовую частоту системы, чтобы медленно синхронизировать синхронизацию времени с внешним источником синхронизации? Будет ли такая вещь влиять как на таймеры, так и на таймер настенных часов?
(*) Я понимаю, что на некоторых архитектурах System.nanoTime()
по необходимости будет использовать тот же механизм, что и System.currentTimeMillis()
. Я также считаю справедливым предположить, что любой современный сервер Windows не является такой аппаратной архитектурой. Это плохое предположение?
(**) Конечно, System.currentTimeMillis()
обычно имеет гораздо больший джиттер, чем System.nanoTime()
, поскольку его зернистость не составляет 1 мс в большинстве систем.