I'm trying to make a ScheduledExecutorService where only one task is active at a time and only once a task has finished, the next task will begin its delay with an arbitrary delay amount.
As a very simple example of what I mean, take a look at this method. The idea is to schedule 10 Runnables to simulate a countdown from 10-1. Each interval takes one second (imagine this was an arbitrary amount of seconds though, I can't use scheduleAtFixedRate in my use case).
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public void startCountdown() {
for (int i = 10; i > 0; i--) {
int countdownNumber = i;
scheduler.schedule(() -> {
System.out.println(countdownNumber);
}, 1, TimeUnit.SECONDS);
}
}
However, this will simply print all 10 numbers at once, instead of waiting for a second between each value. The only way I can circumvent this (to my knowledge) is calculating the ABSOLUTE delay, as opposed to the relative one.
While it's possible to calculate the absolute time for each item, it would be quite a hassle. Isn't there some construct in Java that allows me to queue many items at once, but waits in between each item for the delay to finish, rather than processing every delay at once?
tl;dr
scheduleAtFixedRateto schedule your tasks for an increasing number of seconds. No need for executor service to be single-threaded.Details
Task that re-schedules itself
If you have an arbitrary amount of time not known up front when beginning the scheduling, then you should only run one task at a time. Let the task re-schedule itself.
To enable a task to re-schedule itself, pass a reference to the
ScheduledExecutorServiceto the task object (yourRunnableorCallable) as an argument in the constructor. After the task completes its main work, it discovers/calculates the amount of time to elapse for the next run. The task then submits itself (this) to the passed executor service, along with the amount of time to elapse before the next task execution.I have already posted Answers on Stack Overflow with code for tasks that re-schedule themselves. I would expect others have as well. Search to learn more.
Regarding the "countdown" aspect of your Question, read on.
Countdown
You have the right approach in using a scheduled executor service. The problem is that you are calling the wrong method on that class.
Your call to
schedulemeans you are scheduling several tasks to all run after a single second. All those tasks are starting from the moment your call is made. So each runs after one second from your call toschedule. So the ten tasks are all waiting a second from almost the same moment: ten moments a split-second apart, the split-second being the time it takes for yourforloop to continue.scheduleAtFixedRateThe method you are looking for is
scheduleAtFixedRate. To quote the doc:Notice how this approach does not require the
ScheduledExecutorServiceto be single-threaded.Full example
Here is a complete example app.
When run:
By the way, know that scheduled tasks do not always fire exactly on time for a variety of reasons.
Also, be aware that messages sent to
System.outacross threads do not always appear on the console chronologically. If you care about order, always include and study a timestamp such asInstant#now.