The Executor framework provides the ThreadPoolExecutor class to execute concurrent tasks using a pool of threads that avoids you all the thread creation operations. When you send a task to the executor, according to its configuration, it executes the task as soon as possible. When it ends, the task is deleted from the executor and, if you want to execute them again, you have to send it again to the executor.
But the Executor framework provides the possibility of executing periodic tasks through the ScheduledThreadPoolExecutor class. In this post, you will learn how to use this functionality of that class to schedule a periodic task.
Run task periodically with Timer class
Before that, we use Timer and TimerTask class to execute task periodically and see how it differ from ThreadPoolExecutor. In order to schedule task, you need to extend TimerTask class which provide basic 3 method :
- run method for the action to performed by the Timer class
- cancel method to cancel the timer task
- scheduledExecutionTime to scheduled execution time of the most recent actual execution of this task.
If your program does not contain any termination point it will execute infinite time. So in order to cancel after specific time, we use sleep() method to wait for specific time and cancel() method to stop the execution of the timer task.
Run task periodically with ScheduledThreadPoolExecutor
Main starting at Fri Aug 01 13:05:09 IST 2014
Main: Delay: 999
Main: Delay: 495
Periodic-Task starting at Fri Aug 01 13:05:10 IST 2014
Main: Delay: 1994
Main: Delay: 1494
Main: Delay: 993
Main: Delay: 493
Periodic-Task starting at Fri Aug 01 13:05:12 IST 2014
Main: Delay: 1992
Main: Delay: 1491
Main: Delay: 991
Main: Delay: 490
Periodic-Task starting at Fri Aug 01 13:05:14 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:16 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:18 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:20 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:22 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:24 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:26 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:28 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:30 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:32 IST 2014
Periodic-Task starting at Fri Aug 01 13:05:34 IST 2014
Main ends at Fri Aug 01 13:05:34 IST 2014
How it works...
When you want to execute a periodic task using the Executor framework, you need a ScheduledExecutorService object. To create it (as with every executor), Java recommends the use of the Executors class. This class works as a factory of executor objects. In this case, you should use the newScheduledThreadPool() method to create a ScheduledExecutorService object. That method receives as a parameter the number of threads of the pool. As you have only one task in this example, you have passed the value 1 as a parameter.
Once you have the executor needed to execute a periodic task, you send the task to the executor. You have used the scheduledAtFixedRate() method.
This method accepts four parameters:
- the task you want to execute periodically,
- the delay of time until the first execution of the task,
- the period between two executions,
- the time unit of the second and third parameters. It's a constant of the TimeUnit class.
The TimeUnit class is an enumeration with the following constants: DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, and SECONDS.
The method scheduleAtFixedRate() returns a ScheduledFuture object, which extends the Future interface, with methods to work with scheduled tasks. ScheduledFuture is a parameterized interface.
In this example, as your task is a Runnable object that is not parameterized, you have to parameterize them with the ? symbol as a parameter. You have used one method of the ScheduledFuture interface. The getDelay() method returns the time until the next execution of the task. This method receives a TimeUnit constant with the time unit in which you want to receive the results.
An important point to consider is that the period between two executions is the period of time between these two executions that begins. If you have a periodic task that takes 5 sceconds to execute and you put a period of 3 seconds, you will have two instances of the task executing at a time.
You can see the task executing every 2 seconds (denoted with Task: prefix) and the delay written in the console every 500 milliseconds. That's how long the main thread has been put to sleep. When you shut down the executor, the scheduled task ends its execution and you don't see more messages in the console.
scheduleWithFixedRate() vs scheduledAtFixedRate()
ScheduledThreadPoolExecutor provides other methods to schedule periodic tasks. It is the scheduleWithFixedRate() method. It has the same parameters as the scheduledAtFixedRate() method, but there is a difference worth noticing.
In the scheduledAtFixedRate() method, the third parameter determines the period of time between the starting of two executions. In the scheduledWithFixedRate() method, parameter determines the period of time between the end of an execution of the task and the beginning of the next execution
How setContinueExistingPeriodicTasksAfterShutdownPolicy() works...
You can also configure the behavior of an instance of the ScheduledThreadPoolExecutor class with the shutdown() method. The default behavior is that the scheduled tasks finish when you call that method. You can change this behavior using the setContinueExistingPeriodicTasksAfterShutdownPolicy() method of the ScheduledThreadPoolExecutor class with a true value. The periodic tasks won't finish upon calling the shutdown() method.
TimerTask vs ScheduledThreadPoolExecutor
The Timer facility manages the execution of deferred ("run this task in 100 ms") and periodic ("run this task every 10ms") tasks. However, Timer has some drawbacks, and ScheduledThreadPoolExecutor should be thought of as its replacement.You can construct a ScheduledThreadPoolExecutor through its constructor or through the newScheduledThreadPool factory.
- Timer can be sensitive to changes in the system clock, ScheduledThreadPoolExecutor is not.
- A Timer creates only a single thread for executing timer tasks. If a timer task takes too long to run, the timing accuracy of other TimerTasks can suffer so long-running task can delay other tasks. ScheduledThreadPoolExecutor can be configured with any number of threads. Furthermore, you have full control over created threads, if you want by providing ThreadFactory.
- Another problem with Timer is that it behaves poorly if a TimerTask throws an unchecked exception. The Timer thread doesn't catch the exception, so an unchecked exception thrown from a TimerTask terminates the timer thread. ScheduledThreadExecutor not only catches runtime exceptions, but it lets you handle them if you want (by overriding afterExecute method from ThreadPoolExecutor). Task which threw exception will be canceled, but other tasks will continue to run.
If you know anyone who has started learning Java, why not help them out! Just share this post with them. Thanks for studying today!...