Renato Ivancic - Software Engineer

Background image

Spring Task Scheduling

Backend

Scheduling

Task scheduling is the process of creating tasks to run in future time. The task can be for just one time execution or it can be repeated with a specified frequency.

@Scheduled

The easiest way of configuring and running tasks is with the @Scheduled annotation.

You should not forget to annotate the @Configuration class with the @EnableScheduling.

Its easy as that:

@Scheduled(fixedDelay = DELAY)
public void scheduledTask() {
  LOG.info("Task is being executed");
}

In the annotation itself you can specify various ways of scheduling:

  • fixedDelay
  • fixedRate
  • initialDelay
  • cron

Concurrency issue

There is nothing wrong using shorthand annotation in your application. You can set up scheduling blazingly fast without boilerplate code. Still, you can define only one executor for @Scheduled annotation in the whole application. Let’s say you have a default configuration for the executor and multiple methods scheduled with above configuration. If one of them takes longer time for execution than its the interval for the others, then they will be delayed until the thread from the first one is free again. Unfortunately Spring framework doesn’t yet support parametrized executors for the @Scheduled annotation.

For example we expect that the tasks in three different Scheduled methods are running like depicted below:

Expected scheduling of Scheduled annotation for multiple methods

But because by default, the scheduler is running only with one thread the Scheduled, tasks will be executed in following order:

Expected scheduling of Scheduled annotation for multiple methods

Solutions

SchedulingConfigurer

If you can easily manage the number of scheduled methods in the application go ahead and use custom executor in @Configuration class. Taken from EnableScheduling documentation. When more control is desired, a @Configuration class may implement SchedulingConfigurer. This allows access to the underlying ScheduledTaskRegistrar instance. For example, the following example demonstrates how to customize the Executor used to execute scheduled tasks:

@Configuration
@EnableScheduling
public class AppConfig implements SchedulingConfigurer {
  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    taskRegistrar.setScheduler(taskExecutor());
  }

  @Bean(destroyMethod="shutdown")
  public Executor taskExecutor() {
    return Executors.newScheduledThreadPool(CONFIGURABLE_SCHEDULED_THREAD_POOL);
  }
}

Nested Async Execution

You can take more conservative approaches as well. Inside of the scheduled method execute whole logic in an async method. This way you do not spend cycles of the scheduling thread. You can not use the result anyway.

TaskScheduler

And If you prefer even more fine grained solution you should use concrete TaskScheduler without the @Scheduled annotation for scheduled tasks.

Task Scheduler

First I will put the diagram of the simple class hiearchy for the ThreadPoolTaskScheduler. This way its easier to understand the dependencies and roles of specific classes, that build up the framework.

ThreadPoolTaskScheduler UML diagram

ThreadPoolTaskScheduler is a wrapper around JSE ScheduledThreadPoolExecutor.

Trigger determines the next execution time based on past execution outcomes or it can be based on any arbitrary conditions.

Spring provides two concrete implementations of teh Trigger interface:

When the ThreadPoolTaskScheduler is configured it has to be initialized before any task/runnable can be scheduled. In the initialize() method the wrapped ScheduledThreadPoolExecutor is instantiated.

Example of how to configure ThreadPoolTaskScheduler and then schedule simple runnable that is executed periodically with the PeriodicTrigger:

@PostConstruct
public void initialize() {

  // Initialization of the scheduler
  ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
  scheduler.setPoolSize(1);
  scheduler.setThreadNamePrefix("testScheduler");
  
  // Initialization of the trigger
  PeriodicTrigger trigger = new PeriodicTrigger(DELAY, MILLISECONDS);
  trigger.setInitialDelay(INITIAL);

  // Initialize the scheduler, before nothing can be scheduled
  scheduler.initialize();

  // Schedule the runnable with the specific trigger
  scheduler.schedule(runnable, trigger);
}

// Test runnable
private Runnable runnable = () -> {
  LOG.trace("Runnable is being executed");
};

Sources

LogicBig.com - Spring - Task Scheduling

Baedlung - A Guide to the Spring Task Scheduler

Baedlung - The @Scheduled Annotation in Spring

HowToDoInJava.com - ScheduledThreadPoolExecutor – Task Scheduling with Executors

Journaldev.com - Java Scheduler ScheduledExecutorService ScheduledThreadPoolExecutor Example

Spring scheduling docs - Task Execution and Scheduling

#Spring #Java