Вопрос-ответ

System.currentTimeMillis vs System.nanoTime

System.currentTimeMillis против System.nanoTime

Точность против. Precision

Что я хотел бы знать, так это должен ли я использовать System.currentTimeMillis() или System.nanoTime() при обновлении позиций моего объекта в моей игре? Их изменение в движении прямо пропорционально времени, прошедшему с момента последнего вызова, и я хочу быть как можно более точным.

Я читал, что между различными операционными системами существуют некоторые серьезные проблемы с разрешением по времени (а именно, что Mac / Linux имеют разрешение почти в 1 мс, в то время как Windows имеет разрешение в 50 мс ??). Я в основном запускаю свои приложения в Windows, и разрешение 50 мс кажется довольно неточным.

Есть ли варианты лучше, чем два, которые я перечислил?

Есть предложения / комментарии?

Переведено автоматически
Ответ 1

Если вы просто ищете чрезвычайно точные измерения прошедшего времени, используйте System.nanoTime(). System.currentTimeMillis() даст вам максимально точное возможное время в миллисекундах, прошедшее с момента начала эпохи, но System.nanoTime() даст вам время с точностью до наносекунды относительно некоторой произвольной точки.

Из документации Java:


public static long nanoTime()

Возвращает текущее значение наиболее точного доступного системного таймера в наносекундах.


Этот метод может использоваться только для измерения прошедшего времени и не связан с каким-либо другим понятием системного времени или времени настенных часов. Возвращаемое значение представляет наносекунды с некоторого фиксированного, но произвольного начального времени (возможно, в будущем, поэтому значения могут быть отрицательными). Этот метод обеспечивает наносекундную точность, но не обязательно наносекундную. Не дается никаких гарантий относительно частоты изменения значений. Различия в последовательных вызовах, которые занимают более 292 лет (263 наносекунды), не позволяют точно вычислить прошедшее время из-за числового переполнения.


Например, чтобы измерить, сколько времени требуется для выполнения некоторого кода:

long startTime = System.nanoTime();    
// ... the code being measured ...
long estimatedTime = System.nanoTime() - startTime;

Смотрите также: JavaDoc System.nanoTime() и JavaDoc System.currentTimeMillis() для получения дополнительной информации.

Ответ 2

Поскольку никто больше не упоминал об этом…

Небезопасно сравнивать результаты System.nanoTime() вызовов между разными JVM, каждая JVM может иметь независимое "исходное" время.

System.currentTimeMillis() вернет (приблизительно) одинаковое значение между JVM, потому что оно привязано к времени системных настенных часов.

Если вы хотите вычислить количество времени, прошедшего между двумя событиями, например, по секундомеру, используйте nanoTime(); изменения в системных настенных часах делают currentTimeMillis() некорректным для этого варианта использования.

Ответ 3

Обновление от Аркадия: Я наблюдал более корректное поведение System.currentTimeMillis() в Windows 7 в Oracle Java 8. Время было возвращено с точностью до 1 миллисекунды. Исходный код в OpenJDK не изменился, поэтому я не знаю, что вызывает лучшее поведение.


Дэвид Холмс из Sun пару лет назад опубликовал статью в блоге, в которой очень подробно рассматриваются Java timing API (в частности, System.currentTimeMillis() и System.nanoTime()), когда вы хотели бы что-то использовать и как они работают внутри.

Внутри виртуальной машины Hotspot: часы, таймеры и планирование событий - Часть I - Windows

Один очень интересный аспект таймера, используемого Java в Windows для API с параметром timed wait, заключается в том, что разрешение таймера может меняться в зависимости от того, какие другие вызовы API могли быть выполнены - в масштабах всей системы (а не только в конкретном процессе). Он показывает пример, где использование Thread.sleep() вызовет это изменение разрешения.

Ответ 4

Как уже говорили другие, currentTimeMillis - это время на часах, которое меняется в связи с переходом на летнее время (нет: переход на летнее время и часовой пояс не связаны с currentTimeMillis, остальное верно), пользователи меняют настройки времени, високосные секунды и синхронизацию времени в Интернете. Если ваше приложение зависит от монотонно возрастающих значений прошедшего времени, вы можете предпочесть nanoTime.

Вы можете подумать, что игроки не будут возиться с настройками времени во время игры, и, возможно, вы будете правы. Но не стоит недооценивать сбои из-за синхронизации времени в Интернете или, возможно, пользователей удаленных рабочих столов. API nanoTime невосприимчив к такого рода сбоям.

Если вы хотите использовать тактовое время, но избегать перебоев из-за синхронизации времени в Интернете, вы могли бы рассмотреть NTP-клиент, такой как Meinberg, который "настраивает" тактовую частоту на обнуление, вместо того, чтобы просто периодически сбрасывать часы.

Я говорю по личному опыту. В приложении погоды, которое я разработал, я получал случайные скачки скорости ветра. Мне потребовалось некоторое время, чтобы понять, что моя временная база была нарушена поведением часов на обычном ПК. Все мои проблемы исчезли, когда я начал использовать nanoTime. Согласованность (монотонность) была более важна для моего приложения, чем необработанная точность или абсолютная.

java