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

7
我们希望每1000秒运行一次任务(假设)。
因此,我们有:
timer.scheduleAtFixedRate(task, delay, interval);

大多数情况下这个功能都很好用。然而,由于这是一个嵌入式系统,用户可以改变实时钟。如果他们将时间设置为在我们设置计时器之后的过去时间,则计时器似乎要等到原始实时日期/时间才能执行。所以如果他们将时间往前调3天,计时器就不会在3天内执行 :(
这种行为是否允许或者是Java库中的缺陷?Oracle javadocs似乎没有提到是否依赖于系统时钟的基础值。
如果是允许的,我们如何发现这个时钟变化并重新安排定时器呢?

1
如果您将时间向前调整3天,它会执行无限次(不完全是这样,但您知道我的意思),试图赶上当前时间!! - pstanton
2个回答

18

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

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

这意味着如果你用ScheduledThreadPoolExecutor替换Timer,你将不会看到Timer的行为。要创建一个,请使用Executors.newScheduledThreadPool()等方法。

因为System.nanoTime()的文档中指出:

该方法只能用于测量经过的时间,并与任何其他系统或挂钟时间概念无关。返回值表示自某个固定但任意起始时间以来的纳秒数 [强调我的]。

至于这是否是Timer中的一个错误,也许吧...

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

但是,使用System.currentTimeMillis()的一个结果是Timer对系统日期/时间敏感...这在JavaDoc中没有说明。


4
+1 看了源代码后得出了相同的结论。可能需要更好地记录下来。 - assylias
看起来正是我需要的,但需要Java 1.5或更高版本。我们只能使用1.4 :( 虽然存在JSR-166的后移版本,但仍然依赖于某种不受日期/时间设置更改影响的实时时钟。仍在寻找是否在我们的环境中存在这样的东西... - The Archetypal Paul
抱歉,我完全不支持1.4 :( 我知道一个名为backport-util-concurrent的包,但是System.nanoTime()确实是另一回事...如果是1.4,我想只有本地代码可以提供它。 - fge

5
这里报道了一个bug http://bugs.sun.com/view_bug.do?bug_id=4290274,当系统时钟被设置为更晚的时间时,任务可能会在没有任何延迟的情况下运行多次,以“赶上”错过的执行。当计算机设置为待机/休眠并恢复应用程序时,确实会发生这种情况(这就是我发现的)。在Java调试器中可以通过暂停计时器线程并恢复它来观察到此行为。请注意保留原有html标签格式,但使内容通俗易懂。

1
同样地,当系统时钟被设置为较晚的时间时,任务可能会运行多次。这似乎取决于具体情况。我们有一个IBM j9系统,在这种情况下不会发生多次运行,而是在稍后的某个时间点触发一次,之后按预期重复执行。尽管如此,我们还没有完全描述清楚这个问题。我们正在考虑转移到ScheduledExecutorService。 - The Archetypal Paul
好的,这很有趣。我们在Windows上使用Oracle Java。 - Henno Vermeulen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接