3

I would like to schedule a periodic task which executes every X hours. I have a service which is written in Java and I was thinking of creating a long running background thread that runs forever as long as the service is up. How can I ensure that we are executing the task once every X hours? Is clock drift on my host an issue I should be worried about? I know that frequency of the clock ticks may change if the CPUs are working hard.

Edit: I was thinking of adding a bean to my spring configuration to spin up the thread which will periodically perform my task.

mewsicalcat
  • 63
  • 1
  • 7
  • 1
    How important is it that a task is fired on the hour? Or is it that it is executed every 60 minutes and how much time is acceptable between them (for example, is it OK to execute at time 0 minutes, time 59.9 minutes, and time 120.3 minutes)? – Thomas Owens Nov 13 '15 at 20:38
  • Some more background - the task does not need to be fired on the hour. We have an SLA of refreshing a report once every 24 hours, so once every approximately 24 hours (being off by seconds or even a couple minutes is acceptable) we need to fire off a batch of requests to some service. – mewsicalcat Nov 14 '15 at 00:03
  • Given that we only care about the elapsed time between the tasks, should clock skew be an issue? – mewsicalcat Nov 14 '15 at 00:04
  • The scheduling APIs you would use in Java or a typical modern operating system already work in terms of absolute time, not CPU cycles; and a best practice is to use NTP to keep the system clock accurate to a few milliseconds. – david Nov 14 '15 at 13:27

3 Answers3

3

Java provides a java.util.Timer class that is designed to execute a task on a background thread. One of the modes of operation is "repeated execution at regular intervals". There are fixed-delay and fixed-rate execution methods that can be used, depending on your exact needs.

Java 5 added a java.util.concurrent.ScheduledThreadPoolExecutor class that is more flexible than Timer, but also offers fixed-delay and fixed-rate execution methods.

If you need such precise timing that these aren't suitable, I'm not sure that Java is an appropriate solution. You would be starting to enter the realm of a real-time system. At this point, you should likely be looking for other options.

Thomas Owens
  • 114,398
  • 98
  • 311
  • 431
0

If you are worried, write a test process and run it on the target platform. Using the feature you plan to use for the real process (like ScheduledExecutorService), schedule a task to log the host time every 24 hours. If the host doesn't use NTP to keep its clock synchronized, perhaps you could also make call to a time-keeping web service and log that too. After a few days, you should have a good sense of whether you need a method to correct for drift.

My guess is that the built-in scheduler will be accurate to less than a second per day.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Good idea! Best way to find out if it'll even be an issue is to test it myself. Thanks :) – mewsicalcat Nov 14 '15 at 00:25
  • @mewsicalcat Timer accuracy and resolution is system-dependent. I wouldn't trust a Raspberry Pi without a bit of testing. For a real server, I wouldn't have given it a second thought. – erickson Nov 14 '15 at 01:37
0

Is clock drift on my host an issue I should be worried about?

Yes, clock drift can be an issue when using ScheduledThreadPoolExecutor.

CronScheduler is specifically designed to be proof against clock drift.

Example usage:

Duration syncPeriod = Duration.ofMinutes(1);
CronScheduler cron = CronScheduler.create(syncPeriod);

// If you need just precisely "once every X hours", irrespective of the
// starting time
cron.scheduleAtFixedRate(0, X, TimeUnit.HOURS, runTimeMillis -> {
    // Do the task
});

// If you need "once every X hours" in terms of wall clock time,
// in some time zone:
ZoneId myTZ = ZoneId.systemDefault();
cron.scheduleAtRoundTimesInDay(Duration.ofHours(X), myTZ, runTimeMillis -> {
    // Do the task
});

See Javadocs for scheduleAtRoundTimesInDay.

leventov
  • 14,760
  • 11
  • 69
  • 98