Java中是否存在任何一致的(单调)Clock实现?

时间:2016-01-13 23:05:13

标签: java java-8 java-time

默认的 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()。

或者,在任何地方都可以使用这样的单调时钟实现吗?我猜想肯定会有很多人面临同样的问题。

结论

感谢鼓舞人心的评论和答案。评论中有几个有趣的点,所以我将在这里总结一下。

1。单调时钟

至于我原来的问题,是的,可能会有一个不受系统时间影响的单调时钟向后跳跃。如上所述,这种实现可以基于System.nanoTime()。过去曾经存在过这种方法的问题,但它应该适用于当今的现代系统。这种方法已经在Time4J库中实现,它们的单调时钟可以很容易地转换为java.time.Clock:

Clock clock = TemporalType.CLOCK.from(SystemClock.MONOTONIC);

2。适当的系统时间控制

可以配置系统时间管理(unix / linux中的ntpd),这样系统时间几乎不会向后移动(如果需要,它会慢下来),然后可以依赖系统时间单调,并且Java中不需要时钟魔法。

我会这样做,因为我的应用程序是服务器端,我可以控制时间。实际上,我经历了我自己安装的实验环境中的异常(只有表面的肤浅知识),它只使用 ntpdate 客户端(如果时间不同,它可以向后跳转) ),而不是 ntpd (可以配置,以便它不会跳回)。

第3。使用序列而不是时钟

当需要跟踪之前发生的强烈关系时,从原子生成的序列中提供事件序列号并且不依赖于挂钟更安全。一旦应用程序在多个节点上运行,它就成为唯一的选择(虽然这不是我的情况)。

2 个答案:

答案 0 :(得分:5)

正如@ the8472所说,关键是让机器上的时间同步(jvm运行的地方)正确。

如果您为客户编程,那么依赖系统时钟真的很危险。 但对于服务器,有一个解决方案 - 您可能需要考虑使用严格配置的NTP。

In here他们基本上解释了NTP会减慢时间而不会倒退。

this NTP documentation说:

  

有时,特别是首次启动ntpd时,错误可能会发生   超过128毫秒。这有时可能导致时钟设置   如果本地时钟时间超过128秒,则向后   相对于服务器。在某些应用程序中,此行为可能是   不能接受的。如果命令行中包含-x选项,则   时钟永远不会被踩踏,只会使用转换修正。

答案 1 :(得分:3)

请注意,nanoTime可能会单调增加,但与墙壁时间无关,例如由于休眠事件,VM暂停和类似的事情。

如果您开始在多台服务器上分发内容,那么由于时钟漂移,在currentMillis上同步也可能会再次让您感到厌烦。

也许您应该考虑控制服务器的系统时间。

或者跟踪事件的相对顺序,与它们被记录的时间分开。