Java 计时器如何在胁迫下行动?

How do Java Timers act under duress?

我有一个简单的计时器,每分钟调用一个方法进行后台处理。它是这样称呼的:

EmailSender singletonSender = new EmailSender(cog);
timer.scheduleAtFixedRate(singletonSender, 60000, 60000);

它每分钟收到一个事件,检查是否有任何理由发送电子邮件,发送电子邮件,然后完成。

这一切都假设系统已启动并且 运行,并且有可用的 CPU 周期。如果计算机坐在那里什么都不做,处理也不花时间,这一切都很容易理解。但是我找不到有关当计算机过于繁忙或进入 sleep/hybernation 模式时它的行为方式的文档。

如果电脑进入睡眠状态会怎样?

在检查了许多令人困惑的日志之后,我终于 运行 进行了测试。如果它打印出日志语句和当前时间。我让计算机在 7:30(大约)时进入睡眠状态,并在 10 分钟后(大约)唤醒它。这是日志跟踪:

BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:24:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:25:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:26:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:27:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:28:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:29:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:30:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:31:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:09
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:40:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:41:28
BACKGROUND EVENTS: Processed 0 background events at Fri Nov 06 07:42:28

它似乎发送了一堆 "catch up" 事件。换句话说,就好像它们在计算机处于睡眠状态时堆积在计算机中,然后一旦机器重新启动它们就会全部涌出。就好像所有的事件都产生了,但由于无法传递它们而受阻,最后,当计算机启动时,它们全部涌出。

这是否被设计为以这种方式工作?显然,这会影响您设计系统的方式。如果您期望每天发生 1440 个事件,那么您最终将获得 1440 个事件,这是合乎逻辑的。然而,如果你只是在做后台处理,并且每次调用都完成了所有等待的后台工作,那么那些额外的调用就没有意义了。您将需要设计一种方法来快速处理那些额外的大量调用。

如果超过一分钟才响应呼叫怎么办?

鉴于上述证据,如果计算机变得非常繁忙,您的事件可能会延迟发送,但您仍然会收到所有事件。所以如果一个事件的处理时间超过一分钟,问题是:你能保证事件不会到达不同的线程吗?换句话说:你确定你的处理程序在最后一次调用完成之前永远不会被调用。 Timer 机制试图隐藏有关您在哪个线程上被调用的详细信息,假设所有调用都是在单个线程上进行似乎是合理的,但是,我再次寻找关于此的明确文档。

我想知道的是:这是严格定义的某个地方吗?或者只是当前版本实现方式的产物?

至少 Timer#scheduleAtFixedRate 的 Javadocs 提出了您观察到的内容:

In fixed-rate execution, each execution is scheduled relative to the scheduled execution time of the initial execution. If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up." In the long run, the frequency of execution will be exactly the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate).