Java ScheduledExecutorService Example
1. Introduction
It is common requirement in software development that we want tasks to run (i) periodically or (ii) start after a fixed delay. Since Java is known for richness of its development platform, it abstracts such requirements in an interface java.util.concurrent.ScheduledExecutorService
and its concrete implementation java.util.concurrent.ScheduledThreadPoolExecutor
In this post we would not just look into these APIs but also compare them against parallel APIs java.util.TimerTask
and java.util.Timer
to help us judiciously choose between APIs.
2. The ScheduledExecutorService
ScheduledExecutorService
is java.util.concurrent.ExecutorService
thus it inherits all the functional abstractions from ExecutorService interface (and its super interfaces!); nevertheless, the difference being, ScheduledExecutorService
can “schedule commands to run after a given delay, or to execute periodically” (Source).
The public API to use ScheduledExecutorService
is pretty straightforward. We can “schedule” a repetitive or delayed task encapsulated in a java.lang.Runnable
or java.util.concurrent.Callable
instance with ScheduledExecutorService
configuring the delays. It is obvious that a Runnable task would be non result bearing one and Callable would produce some result.
An instance of ScheduledThreadPoolExecutor
can be retrieved from the convenient factory API, java.util.concurrent.Executors.newScheduledThreadPool(int corePoolSize)
or its overloaded version Executors.newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
. In the subsequent sections, we will demonstrate these APIs through the way of an example.
3. ScheduledExecutorServicve in Action: Scheduled Database updates
Let us say we need to update the database at the end of each day – at midnight. For instance, if the updating client was started at any particular time on a day, the client would then calculate the duration until that day’s midnight (the delay) and schedule the updater code with ScheduledExecutorService
to fire at the end of expiry of this duration, thereafter it would invoke database updater code every 24 hour which would be configured as “fixed delay” with the ScheduledExecutorService
. So we have an initial delay and thereafter fixed regular delays! And all this requirements can be easily configured with ScheduledExecutorService
. In the subsequent passages we would be calculating: (i) The initial delay and (ii) the fixed delays.
3.1 Initial Delay calculation
Just to re-iterate, we would be calculating the initial duration left until the clock ticks that days midnight. For the sake demonstration we would be using some deprecated APIs from the java.util.Date
class. Let us define our midnight straightaway.
Calculating initial delay
final Date midnight = new Date(); midnight.setHours(23); midnight.setMinutes(59); midnight.setSeconds(59); long initialDelay = new Date(midnight.getTime()-System.currentTimeMillis()).getTime();
We would next need to calculate the 24 hours duration. That is straight forward 24 hours is equivalent to 86400000 milliseconds: private static final long PERIOD = 86400000L;
So now with these initial harness and calculations done we can configure seek the aids of ScheduledExecutorService
.
3.2 Configuring ScheduledExecutorService
We consider java.util.concurrent.Executors
and its static factory methods to retrieve a reference to ScheduledExecutorService
and configure it in the following way:
Configuring ScheduledExecutorService
ScheduledExecutorService execService = Executors.newScheduledThreadPool(5); execService.scheduleAtFixedRate(()->{ /* The repetitive task. In our example the repetitive task is to update database. */ }, initialDelay, PERIOD, TimeUnit.MILLISECONDS);
If we add a repetitive task, then the application would look like the following:
JavaScheduledExecutorServiceExample.java
package scheduledexecutorservice; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class JavaScheduledExecutorServiceExample { public static void main(String[] args) { ScheduledExecutorService execService = Executors.newScheduledThreadPool(5); execService.scheduleAtFixedRate(()->{ //The repetitive task, say to update Database System.out.println("hi there at: "+ new java.util.Date()); }, 0, 1000L, TimeUnit.MILLISECONDS); } }
The application above produces the following output:
4. Miscellaneous Observations
ScheduleExecutorService
APIs take Runnable
or Callable
as the tasks to perform, it is possible that multiple Runnable
and/or Callables
are submitted for execution with ScheduledExecutorService
at same execution time; in this situation tasks are popped out of the work queue in the first-in-first-out (FIFO) order of submission for execution. This immediately point to a practical fact that no real time guarantees can be committed for the execution of tasks.ScheduleExecutorService
are queued up in a work/task que. If the task has not yet been scheduled for execution by the run-time system, then cancelling it does cancels its any future execution, however, it does has a side effect!: the task is not removed from the queue immediately until the delay duration elapses. If the delay duration is significantly high, then this implies clogging of work/task queue especially if it is top-bounded! To avoid such situations, ScheduledExecutorService
can be instructed to remove tasks as soon as they are cancelled. The API to use enable (boolean true) this is: setRemoveOnCancelPolicy(boolean)
5. A comparison with Timer/TimerTask framework
Before ScheduledExecutorService
was released (with JDK5), scheduling delayed or repeating tasks were handled by java.util.Timer
and java.util.TimerTask
APIs. The programming model using these APIs is relatively simple, albeit, it had some drawbacks which we would point out herein:
The task which is intended to be scheduled to execute after fixed delay or at every fixed interval had to be modelled in an instance of TimerTask. This abstract class declares an abstract run()
method which, when implemented, would hold the actual task to be executed!
Then an instance of this TimerTask
implementation would have be “scheduled” with a Timer object for execution using any of the following APIs:
void schedule(TimerTask task, Date time) void schedule(TimerTask task, Date firstTime, long period) void schedule(TimerTask task, long delay) void schedule(TimerTask task, long delay, long period) void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) void scheduleAtFixedRate(TimerTask task, long delay, long period)
As can be observed the overloaded scheduleXXX(...)
can be suitably configured with different configurations to suit the scheduling of the tasks (TimerTask
). Thus, now multiple threads can submit their respective TimerTask
instances for execution with a particular Timer
object and the Timer
would schedule them according to their needs.
This sounds good however, this framework and its design policies does not scale up when either (i) the task submitting threads are too many, effectively meaning too many (javadoc claims “thousands” should not be problematic, though) tasks to execute (ii) or, tasks are long running. Since Timer
schedules all tasks (submitted by different threads) for execution on a single thread that it owns there can is a definite possibility that tasks that are queued up too back in the queue might not be able to be scheduled even if their delays have well passed, simply because (i) there were far too many tasks already lined up before this task or (ii) the tasks, though fewer, took too long to execute so much so that the delay, for this waiting task, was well passed! As can be realized, in all of these situations timings of tasks execution would be going for a toss.
On the contrary since a ScheduledThreadPoolExecutor
depends on a carefully configured pool of threads for executing tasks submitted to it, a ScheduledExecutorService
can schedule multiple tasks concurrently and also parallelly in a multi core machine and so now a long running task would not hog other tasks from their concurrently and/or parallel execution. Note, nevertheless, that if a ScheduledThreadPoolExecutor
is configured with a pool size one, then it would behave similar to Timer
and manifest similar problems as with a Timer
instance.
6. Conclusion
ScheduledExecutorService
and ScheduledThreadPoolExecutor
as APIs in the JDK which would enable scheduling of fixed delay and/or fixed interval tasks for their execution with the run-time.
7. Download the Eclipse Project
This example demonstrated use of ScheduledExecutorService
and ScheduledThreadPoolExecutor
as APIs in the JDK.
You can download the full source code of this example here: ScheduledExecutorServiceExample
Hi bro i used this and its is saying this is deprecated method can you suggest it with the use of calendar in java