javaspringmultithreadingscheduled-tasks

Spring Task Scheduler consecutive tasks


I am new to using the Spring Task Scheduler for executing tasks, so this may be a basic question. I have a list of items that I would like to process within a class that implements Runnable. Here is my task class:

public class ProcessTask<T> implements Runnable {

private String item;

public ProcessTask(String item) {
    System.out.println("Starting process for " + item);
    this.item = item;
}

@Override
public void run() {
    System.out.println("Finishing task for " + item);
}

I would like to process a list of items, each one starting 10 seconds after the previous task started. I understand I could just set each one to run 10 seconds after the previous one was scheduled, however I don't want to rely on that as other processes may cause a task to run prior to 10 seconds elapsing.

So in my main class, I have the following:

        Date end = new Date(cal.getTimeInMillis() + 10000); // this is the time that the task should be fired off, the first one being 10 seconds after the current time

    for(String item : items) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(end);
        System.out.println("Next task fires at " + cal.getTime());
        ProcessTask task = new ProcessTask(item);
        ScheduledFuture<?> future = taskScheduler.schedule(task, end);

        end = new Date(Calendar.getInstance().getTimeInMillis() + 10000);
    }

The first task fires off 10 seconds after the code runs, which is great. But then the rest of the items get scheduled immediately, not waiting 10 seconds. I do understand why that happens - because taskScheduler.schedule is asynchronous so the for loop just continues and the rest of the items get scheduled for 10 seconds later.

I tried having the main thread sleep for a second and check if the ScheduledFuture has completed before scheduling the next task, such as:

while(!future.isDone()) {
     Thread.sleep(1000);
     System.out.println("is future done: " + future.isDone());
}

If I add this block immediately after ScheduledFuture<?> future = taskScheduler.schedule(task, end); in the above block, then future.isDone() always returns false, and the ProcessTask run() method never gets called.

Is there any way I can use the ScheduledFuture to determine if the previous task has ended, but if it hasn't, continue to wait? Is there a better way overall to do this? Thanks in advance.


Solution

  • So you don't know when the task will end, but 10 seconds later, you want the next task to run. So planning it can only be done when that task is done. So, have a base abstract class, which does the plumbing.

    public abstract class ScheduleTaskAfterRun<T> implements Runnable {
        protected void executeContent();
        private Runnable nextTask;
        private taskScheduler; // init somehow, probably by constructor...
    
        public void setNextTask(Runnable r) {
            nextTask = r;
        }
    
        @Override
        public void run() {
            executeContent();
            scheduleNextTask();
        }
    
        private void scheduleNextTask() {
            if(nextTask == null) {
                System.out.println("No task to handle, finished!");
                return;
            }
            Date end = new Date(Calendar.getInstance().getTimeInMillis() + 10000);
            ScheduledFuture<?> future = taskScheduler.schedule(nextTask, end);
        }
    }