当计算机在休眠后唤醒时计划任务运行数千次

Scheduled task runs thousands of times when computer wakes after sleeping

使用 Spring 的 @Scheduled 注释方法并指定固定延迟,如下所示:

@Scheduled(fixedRate = 5000)
public void test() {
    log.info("The time is {}", Instant.now());
}

当我在休眠后唤醒计算机时,任务是 运行 很多次一个接一个。

The time is 2020-07-14T08:00:30.358073400Z
The time is 2020-07-14T08:00:35.358969600Z
The time is 2020-07-14T08:00:40.358066100Z
...
The time is 2020-07-14T08:02:51.806689500Z
The time is 2020-07-14T08:02:51.806689500Z
The time is 2020-07-14T08:02:51.806689500Z
The time is 2020-07-14T08:02:51.806689500Z
The time is 2020-07-14T08:02:51.806689500Z
The time is 2020-07-14T08:02:51.807651500Z
The time is 2020-07-14T08:02:51.807651500Z
...

如何让电脑唤醒时只运行一次?

您可以使用 AtomicBoolean 标志,表明该任务已经 运行:

private final AtomicBoolean running = new AtomicBoolean(false);

然后在你预定的方法中

if (running.compareAndSet(false, true)) {
    log.info("The time is {}", Instant.now());
    running.set(false);
}

来自 compareAndSet(expect, update) 的 javadoc:

Atomically sets the value to the given updated value if the current value == the expected value.

Returns: true if successful. False return indicates that the actual value was not equal to the expected value.

因此,if 将检查当前没有任务 运行,然后才会执行您的代码。

使用:@Scheduled(cron = "*/5 * * * * *").

使用 cron 表达式时,错过的执行不会排队。

此外,就像@Lino 注意到的那样,您指定了 fixedRate 而不是 fixedDelay。将 fixedRate 更改为 fixedDelay 也可以解决您的问题,但请考虑到行为可能会发生变化,因为 fixedDelay 是上次调用结束之间的固定时间段 和下一个 的开始,而 fixedRate 是一个以毫秒为单位的固定周期 调用之间 .