Scheduled task runs thousands of times when computer wakes after sleeping

Viewed 70

Annotating a method with Spring's @Scheduled and specifying a fixed delay, like so:

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

When I wake my computer after sleeping it, the task is run many times one immediately after another.

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
...

How can I make it run only once when the computer wakes up?

2 Answers

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

When using cron expressions the missed executions are not queued.

Also, like @Lino noticed, you specified a fixedRate and not a fixedDelay. Changing fixedRate to fixedDelay also fixes your problem, but take into account that the behaviour might change since fixedDelay is the fixed period in milliseconds between the end of the last invocation and the start of the next while fixedRate is a fixed period in milliseconds between invocations.

You could make use of a AtomicBoolean flag which indicates that the task is already running:

private final AtomicBoolean running = new AtomicBoolean(false);

And then in your scheduled method

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

From the javadoc of compareAndSet(expect, update):

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.

So, the if will check that no task is currently running and only then will execute your code.

Related