ScheduledExecutorService

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:

Repetitive Task
Repetitive Task

4. Miscellaneous Observations

  • Since the 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.
  • On the foregoing, the tasks that are submitted to the 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.

    Download
    You can download the full source code of this example here: ScheduledExecutorServiceExample

    Nawazish Khan

    I am Nawazish, graduated in Electrical Engineering, in 2007. I work as a Senior Software Engineer with GlobalLogic India Ltd (Banglore) in the Telecom/Surveillance domain. A Java Community Process (JCP) member with an unconditional love (platform, technology, language, environment etc does not matter) for computer programming. Extremely interested programming multi-threaded applications, IoT devices (on top of JavaME) and application containers. The latest language of interest being Google Go; and Neo4j as the NoSQL Graph Database solution.
    Subscribe
    Notify of
    guest

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    1 Comment
    Oldest
    Newest Most Voted
    Inline Feedbacks
    View all comments
    vivek singh
    vivek singh
    6 years ago

    Hi bro i used this and its is saying this is deprecated method can you suggest it with the use of calendar in java

    Back to top button