Currently I'm using an Executors.newSingleThreadScheduledExecutor()
to schedule a task periodically. The ScheduledExecutorService
provides two options for this, namely:
A Timer
has very similar methods that do the same thing. The problem is that none of those do exactly what I want.
The task that I want to schedule will often take a significant part of the period and sometimes even exceed it (depending on how much work there is currently).
- I don't want to use
#scheduleWithFixedDelay
because in this case the task computation time and the period will add up, thus the period will almost always be too long (and depend on the task computation time), even if the period isn't exceeded. - So it seems like
#scheduleAtFixedRate
is what I want. However, consider the case where there are several subsequent periods with too long task computation times, or consider a single period with a task computation time that has an order of magnitude of multiple periods. This will result in multiple periods that are too short afterwards because theScheduledExecutorService
or theTimer
tries to catch up. The delay can grow indefinitely and affect many periods afterwards, causing a busy CPU when it's maybe not even necessary anymore.
What I want is #scheduleAtFixedRate
but the period should never be shorter than specified (it shouldn't try to catch up). With the following code I want to demonstrate this with an example.
public final class Test {
private static int i = 0;
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println((System.currentTimeMillis() - start) + ": " + i);
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleWithFixedDelay(() -> {
i++;
if (i == 1) {
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
System.out.println((System.currentTimeMillis() - start) + ": " + i);
}, 0L, 1000L, TimeUnit.MILLISECONDS);
}
}
The output of this code using #scheduleWithFixedDelay
is something like this.
0: 0
5012: 1
6018: 2
7024: 3
8025: 4
9029: 5
10037: 6
11042: 7
12050: 8
...
The output of this code using #scheduleAtFixedRate
is something like this.
0: 0
5024: 1
5024: 2
5024: 3
5024: 4
5025: 5
5025: 6
6025: 7
7024: 8
...
(Ignore the least significant time digits.) What I actually want is a #schedule
method that bahaves like this.
0: 0
5000: 1
5000: 2
6000: 3
7000: 4
8000: 5
9000: 6
10000: 7
11000: 8
...
I think this is similar to how game loops work. The question is, does Java have an inbuilt way, similar to ScheduledExecutorService
or Timer
, to schedule a task like that. If not, is there an easy way to implement this or are there any external libraries that I can use for this, without reinventing the wheel?