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

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



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.


ScheduledExecutorService



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?


ScheduledExecutorService



And also TimerTask is better for this or ScheduledExecutorService?


TimerTask


ScheduledExecutorService





Use something like Quartz instead.
– millimoose
Dec 4 '13 at 23:21




9 Answers
9



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.


java.time


java.util.Calendar


java.util.Date



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


LocalDateTime localNow = LocalDateTime.now();
ZoneId currentZone = ZoneId.of("America/Los_Angeles");
ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
ZonedDateTime zonedNext5 ;
zonedNext5 = zonedNow.withHour(5).withMinute(0).withSecond(0);
if(zonedNow.compareTo(zonedNext5) > 0)
zonedNext5 = zonedNext5.plusDays(1);

Duration duration = Duration.between(zonedNow, zonedNext5);
long initalDelay = duration.getSeconds();

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new MyRunnableTask(), initalDelay,
24*60*60, TimeUnit.SECONDS);



The initalDelay 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.


initalDelay


TimeUnit.SECONDS


duration.toMillis()


TimeUnit.MILLISECONDS



And also TimerTask is better for this or ScheduledExecutorService?



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


ScheduledExecutorService


TimerTask



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


ScheduledExecutorService


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


execute


ScheduledExecutorService


awaitTermination


shutdown



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





Thanks for suggestion, can you please explain me in detail how intDelayInHour means that I will be running my task at 5 AM in the morning?
– AKIWEB
Dec 5 '13 at 8:05


intDelayInHour





@TrekkieTechieT-T, please check the edit
– Sage
Dec 5 '13 at 8:17





What is the purpose of aDate?
– José Andias
Jul 29 '14 at 9:52





But if you start this at HH:mm the task will be run at 05:mm as opposed to 5am? It also doesn't account for daylight saving time as OP requested. Ok if you start it immediately after the hour, or if you're happy with any time between 5 and 6, or if you don't mind restarting the application in the middle of the night twice a year after the clocks have changed I suppose...
– PaddyD
Nov 6 '14 at 12:01






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.
– PaddyD
Nov 9 '14 at 16:05


scheduleAtFixedRate



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);





For readability I would suggest TimeUnit.DAYS.toMinutes(1) instead of the "magic number" 1440.
– philonous
Nov 28 '16 at 10:26


TimeUnit.DAYS.toMinutes(1)





Thanks, Victor. In this way need to restart twice a year if I want it to run at the right local time?
– invzbl3
Aug 12 at 23:13






the fixed rate should not change when local time changes, after created, it becomes about the rate.
– Victor
Aug 12 at 23:23



If you don't have the luxury of being able to use Java 8, the following will do what you need:


public class DailyRunnerDaemon

private final Runnable dailyTask;
private final int hour;
private final int minute;
private final int second;
private final String runThreadName;

public DailyRunnerDaemon(Calendar timeOfDay, Runnable dailyTask, String runThreadName)

this.dailyTask = dailyTask;
this.hour = timeOfDay.get(Calendar.HOUR_OF_DAY);
this.minute = timeOfDay.get(Calendar.MINUTE);
this.second = timeOfDay.get(Calendar.SECOND);
this.runThreadName = runThreadName;


public void start()

startTimer();


private void startTimer();

new Timer(runThreadName, true).schedule(new TimerTask()

@Override
public void run()

dailyTask.run();
startTimer();

, getNextRunTime());



private Date getNextRunTime()




It doesn't require any external libs, and will account for daylight savings. Simply pass in the time of day you want to run the task as a Calendar object, and the task as a Runnable. For example:


Calendar


Runnable


Calendar timeOfDay = Calendar.getInstance();
timeOfDay.set(Calendar.HOUR_OF_DAY, 5);
timeOfDay.set(Calendar.MINUTE, 0);
timeOfDay.set(Calendar.SECOND, 0);

new DailyRunnerDaemon(timeOfDay, new Runnable()

@Override
public void run()

try

// call whatever your daily task is here
doHousekeeping();

catch(Exception e)

logger.error("An error occurred performing daily housekeeping", e);


, "daily-housekeeping");



N.B. the timer task runs in a Daemon thread which is not recommended for doing any IO. If you need to use a User thread, you will need to add another method which cancels the timer.



If you have to use a ScheduledExecutorService, simply change the startTimer method to the following:


ScheduledExecutorService


startTimer


private void startTimer()

Executors.newSingleThreadExecutor().schedule(new Runnable()

Thread.currentThread().setName(runThreadName);
dailyTask.run();
startTimer();
, getNextRunTime().getTime() - System.currentTimeMillis(),
TimeUnit.MILLISECONDS);



I am not sure of the behaviour but you may need a stop method which calls shutdownNow if you go down the ScheduledExecutorService route, otherwise your application may hang when you try to stop it.


shutdownNow


ScheduledExecutorService





I got your point. +1 and thank you. However, it is better if we don't use Daemon thread(i.e., new Timer(runThreadName, true)).
– Sage
Nov 9 '14 at 20:58


new Timer(runThreadName, true)





@Sage no worries. A daemon thread is fine if you're not doing any IO. The use case I wrote this for was just a simple fire-and-forget class for kicking off some threads to perform some daily housekeeping tasks. I suppose if you are performing database reads in the timer task thread as OP's request might indicate, then you shouldn't use a Daemon and will need some kind of stop method which you will have to call to enable your application to terminate. stackoverflow.com/questions/7067578/…
– PaddyD
Nov 10 '14 at 10:10





@PaddyD Was the last part i.e the one using ScheduledExecutorSerive correct??? The way the anonymous class is created doesnt look correct syntax wise. Also newSingleThreadExecutor() wouldnt have schedule method right??
– FReeze FRancis
Apr 27 '17 at 12:03



Have you considered using something like Quartz Scheduler? This library has a mechanism for scheduling tasks to run at a set period of time every day using a cron like expression (take a look at CronScheduleBuilder).


CronScheduleBuilder



Some example code (not tested):


public class GetDatabaseJob implements InterruptableJob

public void execute(JobExecutionContext arg0) throws JobExecutionException

getFromDatabase();



public class Example

public static void main(String args)

JobDetails job = JobBuilder.newJob(GetDatabaseJob.class);

// Schedule to run at 5 AM every day
ScheduleBuilder scheduleBuilder =
CronScheduleBuilder.cronSchedule("0 0 5 * * ?");
Trigger trigger = TriggerBuilder.newTrigger().
withSchedule(scheduleBuilder).build();

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job, trigger);

scheduler.start();




There's a bit more work upfront, and you may need to rewrite your job execution code, but it should give you more control over how you want you job to run. Also it would be easier to change the schedule should you need to.



Java8:

My upgrage version from top answer:





/**
* Execute @link AppWork once per day.
* <p>
* Created by aalexeenka on 29.12.2016.
*/
public class OncePerDayAppWorkExecutor

private static final Logger LOG = AppLoggerFactory.getScheduleLog(OncePerDayAppWorkExecutor.class);

private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

private final String name;
private final AppWork appWork;

private final int targetHour;
private final int targetMin;
private final int targetSec;

private volatile boolean isBusy = false;
private volatile ScheduledFuture<?> scheduledTask = null;

private AtomicInteger completedTasks = new AtomicInteger(0);

public OncePerDayAppWorkExecutor(
String name,
AppWork appWork,
int targetHour,
int targetMin,
int targetSec
)
this.name = "Executor [" + name + "]";
this.appWork = appWork;

this.targetHour = targetHour;
this.targetMin = targetMin;
this.targetSec = targetSec;


public void start()
scheduleNextTask(doTaskWork());


private Runnable doTaskWork()
return () ->
LOG.info(name + " [" + completedTasks.get() + "] start: " + minskDateTime());
try
isBusy = true;
appWork.doWork();
LOG.info(name + " finish work in " + minskDateTime());
catch (Exception ex)
LOG.error(name + " throw exception in " + minskDateTime(), ex);
finally
isBusy = false;

scheduleNextTask(doTaskWork());
LOG.info(name + " [" + completedTasks.get() + "] finish: " + minskDateTime());
LOG.info(name + " completed tasks: " + completedTasks.incrementAndGet());
;


private void scheduleNextTask(Runnable task)
LOG.info(name + " make schedule in " + minskDateTime());
long delay = computeNextDelay(targetHour, targetMin, targetSec);
LOG.info(name + " has delay in " + delay);
scheduledTask = executorService.schedule(task, delay, TimeUnit.SECONDS);


private static long computeNextDelay(int targetHour, int targetMin, int targetSec)
ZonedDateTime zonedNow = minskDateTime();
ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec).withNano(0);

if (zonedNow.compareTo(zonedNextTarget) > 0)
zonedNextTarget = zonedNextTarget.plusDays(1);


Duration duration = Duration.between(zonedNow, zonedNextTarget);
return duration.getSeconds();


public static ZonedDateTime minskDateTime()
return ZonedDateTime.now(ZoneId.of("Europe/Minsk"));


public void stop()
LOG.info(name + " is stopping.");
if (scheduledTask != null)
scheduledTask.cancel(false);

executorService.shutdown();
LOG.info(name + " stopped.");
try
LOG.info(name + " awaitTermination, start: isBusy [ " + isBusy + "]");
// wait one minute to termination if busy
if (isBusy)
executorService.awaitTermination(1, TimeUnit.MINUTES);

catch (InterruptedException ex)
LOG.error(name + " awaitTermination exception", ex);
finally
LOG.info(name + " awaitTermination, finish");






I had a similar problem. I had to schedule bunch of tasks that should be executed during a day using ScheduledExecutorService.
This was solved by one task starting at 3:30 AM scheduling all other tasks relatively to his current time. And rescheduling himself for the next day at 3:30 AM.


ScheduledExecutorService



With this scenario daylight savings are not an issue anymore.



You can use a simple date parse, if the time of the day is before now, let's start tomorrow :


String timeToStart = "12:17:30";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss");
SimpleDateFormat formatOnlyDay = new SimpleDateFormat("yyyy-MM-dd");
Date now = new Date();
Date dateToStart = format.parse(formatOnlyDay.format(now) + " at " + timeToStart);
long diff = dateToStart.getTime() - now.getTime();
if (diff < 0)
// tomorrow
Date tomorrow = new Date();
Calendar c = Calendar.getInstance();
c.setTime(tomorrow);
c.add(Calendar.DATE, 1);
tomorrow = c.getTime();
dateToStart = format.parse(formatOnlyDay.format(tomorrow) + " at " + timeToStart);
diff = dateToStart.getTime() - now.getTime();


ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new MyRunnableTask(), TimeUnit.MILLISECONDS.toSeconds(diff) ,
24*60*60, TimeUnit.SECONDS);



Just to add up on Victor's answer.



I would recommend to add a check to see, if the variable (in his case the long midnight) is higher than 1440. If it is, I would omit the .plusDays(1), otherwise the task will only run the day after tomorrow.


midnight


1440


.plusDays(1)



I did it simply like this:


Long time;

final Long tempTime = LocalDateTime.now().until(LocalDate.now().plusDays(1).atTime(7, 0), ChronoUnit.MINUTES);
if (tempTime > 1440)
time = LocalDateTime.now().until(LocalDate.now().atTime(7, 0), ChronoUnit.MINUTES);
else
time = tempTime;



The following example work for me


public class DemoScheduler

public static void main(String args)

// Create a calendar instance
Calendar calendar = Calendar.getInstance();

// Set time of execution. Here, we have to run every day 4:20 PM; so,
// setting all parameters.
calendar.set(Calendar.HOUR, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.AM_PM, Calendar.AM);

Long currentTime = new Date().getTime();

// Check if current time is greater than our calendar's time. If So,
// then change date to one day plus. As the time already pass for
// execution.
if (calendar.getTime().getTime() < currentTime)
calendar.add(Calendar.DATE, 1);


// Calendar is scheduled for future; so, it's time is higher than
// current time.
long startScheduler = calendar.getTime().getTime() - currentTime;

// Setting stop scheduler at 4:21 PM. Over here, we are using current
// calendar's object; so, date and AM_PM is not needed to set
calendar.set(Calendar.HOUR, 5);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.AM_PM, Calendar.PM);

// Calculation stop scheduler
long stopScheduler = calendar.getTime().getTime() - currentTime;

// Executor is Runnable. The code which you want to run periodically.
Runnable task = new Runnable()

@Override
public void run()

System.out.println("test");

;

// Get an instance of scheduler
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// execute scheduler at fixed time.
scheduler.scheduleAtFixedRate(task, startScheduler, stopScheduler, MILLISECONDS);




reference:
https://chynten.wordpress.com/2016/06/03/java-scheduler-to-run-every-day-on-specific-time/






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard