如果时钟发生变化,Timertask.scheduleAtFixedRate应该怎么做?

我们希望每1000秒运行一次任务(比方说)。

所以我们有

timer.scheduleAtFixedRate(task, delay, interval); 

大多数情况下,这很好。 但是,这是一个嵌入式系统,用户可以更改实时时钟。 如果他们在我们设置定时器之后将其设置为过去的时间,则计时器似乎直到原始实时日期/时间才执行。 因此,如果他们将其设置为3天,则计时器不会执行3天:(

这是允许的行为,还是Java库中的缺陷? Oracle javadoc似乎没有提及有关依赖性的任何信息,也没有提及系统时钟的基础值。

如果允许的话,我们如何发现这个时钟变化并重新安排我们的计时器?

查看Timer for Java 1.7的源代码,它似乎使用System.currentTimeMillis()来确定下一次执行任务。

但是,查看ScheduledThreadPoolExecutor的源代码,它使用System.nanoTime()

这意味着如果您使用一个Timer代替Timer ,则不会看到该行为。 要创建一个,请使用例如Executors.newScheduledThreadPool()

为什么你不会看到这种行为是因为System.nanoTime()的文档说的是:

此方法只能用于测量经过的时间,与系统或挂钟时间的任何其他概念无关。 返回的值表示纳秒,因为一些固定但任意的原始时间 [强调我的]。

至于这是否是Timer的错误,也许……

请注意,与ScheduledExecutorService不同, Timer支持绝对时间,这可能解释了它对System.currentTimeMillis() ; 此外,自Java 1.3以来, Timer一直存在,而System.nanoTime()仅出现在1.5中。

但是使用System.currentTimeMillis()Timer对系统日期/时间敏感……并且javadoc中没有记录。

这里有报道http://bugs.sun.com/view_bug.do?bug_id=4290274

类似地,当系统时钟设置为稍后时间时,任务可以多次运行而没有任何延迟以“赶上”错过的执行。 当计算机设置为待机/hibernate并且应用程序恢复时(这就是我发现的),这恰好发生了这种情况。

通过挂起计时器线程并恢复它,也可以在Java调试器中看到此行为。