Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run certain task every day at a particular time using ScheduledExecutorService?

I am trying to run a certain task everyday at 5 AM in the morning. So I decided to use ScheduledExecutorService for this but so far I have seen examples which shows how to run task every few minutes.

And I am not able to find any example which shows how to run a task every day at a particular time (5 AM) in the morning and also considering the fact of daylight saving time as well -

Below is my code which will run every 15 minutes -

public class ScheduledTaskExample {     private final ScheduledExecutorService scheduler = Executors         .newScheduledThreadPool(1);      public void startScheduleTask() {     /**     * not using the taskHandle returned here, but it can be used to cancel     * the task, or check if it's done (for recurring tasks, that's not     * going to be very useful)     */     final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(         new Runnable() {             public void run() {                 try {                     getDataFromDatabase();                 }catch(Exception ex) {                     ex.printStackTrace(); //or loggger would be better                 }             }         }, 0, 15, TimeUnit.MINUTES);     }      private void getDataFromDatabase() {         System.out.println("getting data...");     }      public static void main(String[] args) {         ScheduledTaskExample ste = new ScheduledTaskExample();         ste.startScheduleTask();     } } 

Is there any way, I can schedule a task to run every day 5 AM in the morning using ScheduledExecutorService considering the fact of daylight saving time as well?

And also TimerTask is better for this or ScheduledExecutorService?

like image 260
AKIWEB Avatar asked Dec 04 '13 22:12

AKIWEB


People also ask

How do I schedule a program to run automatically?

To schedule an automatic task on your Windows computer, you'll first have to open the Task Scheduler. Here's what you've to do: Go to the Start menu search bar, type in 'task scheduler,' and select the best match. In the Task Scheduler menu, right-click on the Task Scheduler Library, and select New Folder…

How do I schedule a task to run every 15 minutes?

Go to Control Panel » Administrative Tools » Scheduled Tasks. Create the (basic) task. Go to Schedule » Advanced. Check the box for "Repeat Task" every 10 minutes with a duration of, e.g. 24 hours or Indefinitely.

How do you schedule a task in Java?

One of the methods in the Timer class is the void schedule(Timertask task, Date time) method. This method schedules the specified task for execution at the specified time. If the time is in the past, it schedules the task for immediate execution.


2 Answers

As with the present java SE 8 release with it's excellent date time API with java.time these kind of calculation can be done more easily instead of using java.util.Calendar and java.util.Date.

  • Use date time class's i.e. LocalDateTime of this new API
  • Use ZonedDateTime class to handle Time Zone specific calculation including Daylight Saving issues. You will find tutorial and example here.

Now as a sample example for scheduling a task with your use case:

ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")); ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0); if(now.compareTo(nextRun) > 0)     nextRun = nextRun.plusDays(1);  Duration duration = Duration.between(now, nextRun); long initialDelay = duration.getSeconds();  ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);             scheduler.scheduleAtFixedRate(new MyRunnableTask(),     initialDelay,     TimeUnit.DAYS.toSeconds(1),     TimeUnit.SECONDS); 

The initialDelay is computed to ask the scheduler to delay the execution in TimeUnit.SECONDS. Time difference issues with unit milliseconds and below seems to be negligible for this use case. But you can still make use of duration.toMillis() and TimeUnit.MILLISECONDS for handling the scheduling computaions in milliseconds.

And also TimerTask is better for this or ScheduledExecutorService?

NO: ScheduledExecutorService seemingly better than TimerTask. StackOverflow has already an answer for you.

From @PaddyD,

You still have the issue whereby you need to restart this twice a year if you want it to run at the right local time. scheduleAtFixedRate won't cut it unless you are happy with the same UTC time all year.

As it is true and @PaddyD already has given a workaround(+1 to him), I am providing a working example with Java8 date time API with ScheduledExecutorService. Using daemon thread is dangerous

class MyTaskExecutor {     ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);     MyTask myTask;     volatile boolean isStopIssued;      public MyTaskExecutor(MyTask myTask$)      {         myTask = myTask$;              }          public void startExecutionAt(int targetHour, int targetMin, int targetSec)     {         Runnable taskWrapper = new Runnable(){              @Override             public void run()              {                 myTask.execute();                 startExecutionAt(targetHour, targetMin, targetSec);             }                      };         long delay = computeNextDelay(targetHour, targetMin, targetSec);         executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);     }      private long computeNextDelay(int targetHour, int targetMin, int targetSec)      {         LocalDateTime localNow = LocalDateTime.now();         ZoneId currentZone = ZoneId.systemDefault();         ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);         ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);         if(zonedNow.compareTo(zonedNextTarget) > 0)             zonedNextTarget = zonedNextTarget.plusDays(1);                  Duration duration = Duration.between(zonedNow, zonedNextTarget);         return duration.getSeconds();     }          public void stop()     {         executorService.shutdown();         try {             executorService.awaitTermination(1, TimeUnit.DAYS);         } catch (InterruptedException ex) {             Logger.getLogger(MyTaskExecutor.class.getName()).log(Level.SEVERE, null, ex);         }     } } 

Note:

  • MyTask is an interface with function execute.
  • While stopping ScheduledExecutorService, Always use awaitTermination after invoking shutdown on it: There's always a likelihood your task is stuck / deadlocking and the user would wait forever.

The previous example I gave with Calender was just an idea which I did mention, I avoided exact time calculation and Daylight saving issues. Updated the solution on per the complain of @PaddyD

like image 97
Sage Avatar answered Sep 19 '22 13:09

Sage


In Java 8:

scheduler = Executors.newScheduledThreadPool(1);  //Change here for the hour you want ----------------------------------.at()        Long midnight=LocalDateTime.now().until(LocalDate.now().plusDays(1).atStartOfDay(), ChronoUnit.MINUTES); scheduler.scheduleAtFixedRate(this, midnight, 1440, TimeUnit.MINUTES); 
like image 30
Victor Avatar answered Sep 21 '22 13:09

Victor