默认java.time.Clock实现基于System.currentTimeMillis()。如这里所讨论的,
Monotonically increasing time in 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()是单调的(如果底层系统支持它)。所以,如果我想将我的代码放在java.time API上,那我需要在内部使用nanoTime的Clock来确保单向流程。也许这样的事情会起作用。还是不行?
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()。
或者,是否有任何地方可以实现这样一个单调的时钟实现?我想呢肯定有很多人面对同样的问题。
结论
感谢您的鼓舞人心的意见和答案。有几个有趣的点分散在评论中,所以我将在这里总结一下。
单调时钟
对于我原来的问题,是的,可以让单调时钟不受系统时间的影响。这样的实现可以基于我上面提出的System.nanoTime()。过去曾经有这样的问题,但今天的现代制度应该是正常的。这种方法已经在Time4J库中实现了,它们的单调时钟可以很容易地转换成java.time.Clock:
Clock clock = TemporalType.CLOCK.from(SystemClock.MONOTONIC);
适当的系统时间控制
可以配置系统时间管理(unix / linux中的ntpd),以便系统时间几乎不会回退(如果需要的话,系统时间就会慢下来),那么可以依靠系统时间是单调的, Java是必需的。
我会这样做,因为我的应用程序是服务器端,我可以控制时间。实际上,我在一个实验环境中遇到了异常,我自己安装了(只有表面上的领域知识),而且它只是使用ntpdate客户端(如果时间不同步,它可以向后跳),而不是ntpd可以进行配置,使其不会跳回)。
3.使用序列而不是时钟
当需要跟踪强烈关系之前发生的事情,从原子生成的序列中提供事件序列号并不依赖于挂钟更安全。一旦应用程序在几个节点上运行(这不是我的情况),它就成为唯一的选择。