Description
Observed behaviour
ThreadPoolTaskScheduler
is used for methods annotated with @Scheduled
.
In the example of a method which is to be run at a specific time e.g. @Scheduled( cron = "0 0 2 * * *" )
(run at 2 a.m.), the ScheduledThreadPoolExecutor
within ThreadPoolTaskScheduler
knows that it must execute this at the future time.
However, when I call threadPoolTaskScheduler.shutdown()
the shutdown is being blocked until the queued task has been exectued. In the above example this would be, 2 a.m. + the time it takes to execute the task. Note that this behaviour only happens when I have configured a long enough termination period with threadPoolTaskScheduler.setAwaitTerminationSeconds
.
One other point. I know the ThreadPoolTaskScheduler
will get automatically shutdown when the application shuts down, but I am doing it manually for other reasons which I won't go into in this issue.
In summary:
- I have a method scheduled for execution in the future
- my
ThreadPoolTaskScheduler
has a long terimation period (setAwaitTerminationSeconds
) - before that time I'm manually calling
threadPoolTaskScheduler.shutdown()
- shutdown is blocking until the scheduled time
My requirement here is that the ThreadPoolTaskScheduler
should shut down faster by only waiting for those tasks which are currently being processed. Tasks which are scheduled for a future time will not be executed.
Current workaround
I found a method on ScheduledThreadPoolExecutor
named setExecuteExistingDelayedTasksAfterShutdownPolicy
which seems to do what I need.
* Sets the policy on whether to execute existing delayed * tasks even when this executor has been {@code shutdown}. * In this case, these tasks will only terminate upon * {@code shutdownNow}, or after setting the policy to * {@code false} when already shutdown. * This value is by default {@code true}.
When I set this value to false
by configuring a custom ThreadPoolTaskScheduler
I observe the desired behaviour.
final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler()
{
@Override
protected ExecutorService initializeExecutor(final ThreadFactory threadFactory, final RejectedExecutionHandler rejectedExecutionHandler)
{
final ExecutorService executorService = super.initializeExecutor(threadFactory, rejectedExecutionHandler);
if (executorService instanceof ScheduledThreadPoolExecutor)
{
((ScheduledThreadPoolExecutor) executorService).setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
}
return executorService;
}
};
Suggested improvement
Could we configure this more easily using a property on the ThreadPoolTaskScheduler
? In the ThreadPoolTaskScheduler
method initializeExecutor
it already does similar configurations e.g. for the removeOnCancelPolicy
if (this.removeOnCancelPolicy) {
if (this.scheduledExecutor instanceof ScheduledThreadPoolExecutor) {
((ScheduledThreadPoolExecutor) this.scheduledExecutor).setRemoveOnCancelPolicy(true);
}
else {
logger.debug("Could not apply remove-on-cancel policy - not a ScheduledThreadPoolExecutor");
}
}
If being able to set this configuration would be valuable for this framework, then maybe this would be a suitable place to put it?
The end result of how to use the new functionality might look like this:
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler()
{
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
return scheduler;
}