По умолчанию реализация java.time.Clock основана на System.currentTimeMillis(). Как обсуждалось здесь, например, Монотонное увеличение времени на Java?, не гарантируется монотонность.
И действительно, я регулярно испытываю ситуацию, когда системное время автоматически корректируется на несколько секунд в прошлое, а часы java тоже возвращаются.
//now() returns 2016-01-13T22:34:05.681Z
order.setCreationTime(Instant.now());
//... something happens, order gets cancelled
//now() returns 2016-01-13T22:34:03.123Z
//which is a few seconds before the former one,
//even though the call was performed later - in any reasonable sense.
//The recorded history of events is obviously inconsistent with the real world.
order.setCancelationTime(Instant.now());
Затем невозможно выполнять зависящие от времени вещи, такие как запись и анализ истории событий, когда нельзя полагаться на время только в одном направлении.
В вышеприведенном сообщении говорится, что System.nanoTime() является монотонным (если базовая система его поддерживает). Поэтому, если я хочу использовать свой код в API java.time, мне понадобится Clock, который использует nanoTime для обеспечения одностороннего потока времени. Может быть, что-то вроде этого будет работать. Или это не так?
public class MyClock() extends java.time.Clock {
private final long adjustment;
public MyClock() {
long cpuTimeMillis = System.nanoTime()/1000000;
long systemTimeMillis = System.currentTimeMillis();
adjustment = systemTimeMillis - cpuTimeMillis;
}
@Override
public long millis() {
long currentCpuTimeMillis = System.nanoTime()/1000000;
return currentCpuTimeMillis + adjustment;
}
}
Это всего лишь эскиз, а не полная реализация часов. И я полагаю, что правильная реализация также должна выполнять настройку против другого Clock, переданного в конструкторе, а не непосредственно против currentTimeMillis().
Или, существует ли такая монотонная реализация Clock в любом месте? Я бы предположил, что, должно быть, было много людей, столкнувшихся с одной и той же проблемой.
Заключение
Спасибо за вдохновляющие комментарии и ответы. Есть несколько интересных моментов, разбросанных по комментариям, поэтому я опишу их здесь.
1. Монотонные часы
Что касается моего первоначального вопроса, да, можно иметь монотонные часы, на которые не влияет системное время, отбрасывающее назад. Такая реализация может быть основана на System.nanoTime(), как я предложил выше. Раньше были проблемы с этим aproach в прошлом, но на современных современных системах он должен хорошо работать. Этот подход уже реализован, например, в библиотеке Time4J, их монотонные часы могут быть легко преобразованы в java.time.Clock:
Clock clock = TemporalType.CLOCK.from(SystemClock.MONOTONIC);
2. Правильное системное управление временем
Можно настроить системное управление временем (ntpd в unix/linux), так что системное время практически никогда не возвращается (при необходимости оно просто замедляется), тогда можно полагаться на то, что время системы монотонно и в Java нет часового магии.
Я пойду так, потому что мое приложение является серверным, и я могу контролировать время. Фактически, я испытал аномалии в экспериментальной среде, в которой я установил себя (только с поверхностным знанием домена), и использовал только клиент ntpdate (который может перепрыгнуть назад, если время не синхронизировано), а не ntpd (что может быть сконфигурирован так, чтобы он не возвращался назад).
3. Использование последовательностей, а не часов
Когда нужно отслеживать сильное отношение, что было до этого, безопаснее давать серийные номера событий из атомарно генерируемой последовательности и не полагаться на настенные часы. Он становится единственным вариантом, когда приложение работает на нескольких узлах (это не мое дело, хотя).