Rescheduling a job scheduled with scheduledExecutorService

Rohit Bajaj
Rohit Bajaj used Ask the Experts™
on
Hi,
In my spring mvc application. I am scheduling a job using scheduledExecutorService.
here is code :
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
            SimpleJob simpleJob = new SimpleJob(guid);
            ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(simpleJob,
                    Calendar.getInstance().getTimeInMillis() - calendar.getTimeInMillis()
                    , TimeUnit.MILLISECONDS);
        }

Open in new window


Take for example that above a job is scheduled after 1000000 milliseconds.
Now it may happen that my web application gets a http request saying that reschdule the job for some guid to 2000 milliseconds.
guid is a string i am passing above in the job which identifies a particular job.

Now if the job is already running or has run... i can ignore the request of recheduling.
But suppose it has yet to run.. In that case i want to rechdule it...

How to do it ?

Thanks
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Moussa MokhtariEnterpreneur
Top Expert 2016
Commented:
@Rohit Bajaj the class pendingTaskRunner  takes as arg a specific LocalDateTime to run a specific task
I added also the ReSchedule Option to be able to ReSchedule your task

 
import java.time.LocalDateTime;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Paradox
 */
public class Heart {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        LocalDateTime showdate = LocalDateTime.now().plusSeconds(30);
        
        try {
            pendingTaskRunner pending = new pendingTaskRunner(showdate, new Runnable() {
                @Override
                public void run() {
                    System.out.println("Task is done !,bye");
                }
            });
            pending.start();
            pending.reSchedule(showdate.minusSeconds(10));
        } catch (SecurityException ex) {
            Logger.getLogger(Heart.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

Open in new window



import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Paradox
 */
public class pendingTaskRunner {

    LocalDateTime date;
    ScheduledFuture<?> pendingTask;
    Runnable func;

    public pendingTaskRunner(LocalDateTime ExcuteDate, Runnable func) {
        this.date = ExcuteDate;
        this.func = func;
    }
    public void start() {
        pendingTask = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
            public void run() {
                LocalDateTime local = LocalDateTime.now();
                int duration = date.compareTo(local);
                System.out.println("Not Yet");
                if (duration == 0 || duration < 0 ) {
                    try {
                        func.run();
                        pendingTask.cancel(true);
                    } catch (Exception ex) {
                        Logger.getLogger(Heart.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }, 0, 10, TimeUnit.SECONDS);
    }
     public void stop() {
         pendingTask.cancel(true);
     }
     public void reSchedule(LocalDateTime redate){
         this.date = redate;
         System.out.println("ReSchedule");
     }
}

Open in new window

Rohit,

I think one way to reschedule the task is to store the Future that you are returned and if you need to reschedule the task, cancel the future and submit a new task with the correct delay.

So roughly like this:

private final Map<String, ScheduledFuture> map = new HashMap<> ;

// Then when you schedule the task, hold onto the Future:
ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(...) ;
map.put(guid.toString(), scheduledFuture) ;

Open in new window


Now when later you wish to reschedule:
ScheduledFuture scheduledFuture = map.get(guid.toString()) ;
if (!scheduledFuture.isDone()) {
    scheduledFuture.cancel() ;

    // Now schedule it again at the correct time
    ScheduledFuture newScheduledFuture = scheduledExecutorService.schedule(...) ;
    map.put(guid.toString(), newScheduledFuture) ;
}

Open in new window


Hope that helps,

Doug
IT Business Systems Analyst / Software Developer
Top Expert 2015
Commented:
To handle your general use case (which you've now asked several questions about) I would actually go another way, and NOT do it with anything like ScheduledExecutors, or at least not in the way you are using them.

Firstly, one of your questions mentioned that you have info stored in a table about the messages that you need to send (or something like that). Since you are already using a DB, my suggestion would be to setup a table in the DB that has date/time as a column and details of the task as another column, you could even have the GUID that you describe above as another column so that you can easily find an existing task again. Then you insert a row for each scheduled task that needs to run with the date/time column set to the time when it needs to run.

Then all you need is a basic ScheduledExecutor that runs somewhere between every second (if it really does need to be that accurate, but I doubt it) or every minute, etc. And this then checks the database table to see if there are any rows with date/time LESS THAN the current date/time, if so it does whatever the details in that row instruct it to do, ie. send a message, etc. and then deletes the row (or at least marks it as processed).


This has several advantages with requirements that you have mentioned over your many questions, ie. for rescheduling like you mention in this question, you simply update the date/time in the table row. This also works easily for having multiple tasks waiting to run at different times (or timeszones) as you just have multiple rows in the table, each with the correct SERVER date/time for the task to run, etc.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial