Как я могу измерить время в Java, не подверженное изменениям системных часов?

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

Использование сетевых вызовов не является возможным, так как это возможно очень часто и требуется быстрое возвращение.

Есть ли общее решение для этого?

ИЗМЕНИТЬ

Извините, я должен был объяснить причину. Это не значит, что вы не будете уничтожать злонамеренных пользователей - это то, что было вызвано выводом клиента для простоя и обычных клиентских событий.

Ответ 1

Это не отвечает на ваш вопрос, но ошибка # 6458294 подразумевает, что, когда это возможно, реализация Sun в nanoTime() будет использовать механизмы которые действительно монотонны (CLOCK_MONOTONIC на Linux, QueryPerformanceFrequency/QueryPerformanceCounter в Windows). Только если они недоступны, они возвращаются к механизму, который подвержен изменениям системных часов.

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

Вы также можете прочитать это сообщение в блоге, в котором более подробно обсуждается случай HotSpot-on-Windows.

Ответ 2

Я не думаю, что есть способ сделать это.

Конечно, нет способа сделать это, что невозможно подорвать. По сути, вы находитесь во власти операционной системы и JVM относительно того, что сообщается в приложении Java как текущее время. Либо, либо оба из них могут быть исправлены так, чтобы код Java заканчивался получением фиктивной отметки времени. Вы можете попытаться защитить от этого, но тогда все, что нужно хакеру, - это исправить ваше приложение, чтобы полностью отключить проверку лицензии.

Для записи эта "уязвимость" применяется независимо от того, используете ли вы Java.

Ответ 3

Если вы пытаетесь остановить людей от подрывной схемы лицензии, установив часы назад, вам нужно сохранить наивысшее время, которое вы видели до сих пор в каком-то зашифрованном и защищенном хранилище, а затем отключить программу, если либо время меньше, чем вы наивысшее (с некоторым учетом корректировок часов NTP и изменений в DST) или если они каким-то образом вмешиваются в безопасное хранилище или удаляют его.

Ответ 4

Я не знаю, изменится ли nanoTime(), если системные часы меняются, но я предполагаю, что это возможно. Также nanoTime() может быть неточным.

Если вам действительно нужно защищать от изменения часов, вы можете контролировать часы в потоке. Сон на 100 мс или 1000 мс, затем вызовите currentTimeMillis(). Если часы продвинулись более чем на 1000 + x или пошли назад, то, скорее всего, часы изменились (или нить попала на что-то, что возможно).

В случае дизъюнкции вы можете действительно проверить сетевой вызов. Конечно, возможно, что время сети может измениться из-за вставки прыжков секунд. Я однажды прочитал комментарий Slashdot некоторыми учеными, которые координировали астрономические данные из множества разных источников. Во время эксперимента был добавлен второй прыжок, и он в основном разрушил его, потому что некоторые сайты вставили его, а другие - нет.

Другой возможностью может быть использование низкоуровневого API-интерфейса для получения некоторых других системных таймеров. Такие как время работы системы или сети для калибровки API. У Windows есть функция getTickCount(), которая возвращает число миллисекунд с момента загрузки. В системах Unix вы можете использовать команду uptime для получения приблизительной оценки. Вы можете периодически проверять эти значения, чтобы увидеть, изменились ли системные часы.

Ответ 5

Если вы не возражаете добавить немного родного кода в свое Java-приложение, используйте:

  • QueryPerformanceCounter() и QueryPerformanceFrequency() в Windows; или

  • Функция POSIX clock_gettime() с идентификатором часов CLOCK_MONOTONIC.

Обратите внимание, что использование x86 TSC register не рекомендуется на многопроцессорных системах, поэтому лучше использовать выше API.