Spring Boot @Scheduled Task Execution Example

To schedule jobs in a Spring Boot application to run periodically, Spring Boot provides @EnableScheduling and @Scheduled annotations. Let us learn to use Spring boot @Scheduled annotation.

See Also: @Scheduled Jobs with Spring Batch

1. Maven

The @Scheduled annotation is part of Spring core so it can be imported with the spring-boot-starter dependency itself:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>

2. Enable @Scheduled Annotation with @EnableScheduling

The annotation support for task scheduling is enabled using the @EnableScheduling annotation in a @Configuration class. It internally imports the SchedulingConfiguration via the @Import(SchedulingConfiguration.class) instruction. It registers the necessary beans.

@EnableScheduling
@Configuration
public class SchedulerConfig {

}

When more control is desired, we can implement SchedulingConfigurer interface which allows access to the underlying ScheduledTaskRegistrar instance.

In the following example, we customize the task execution of scheduled tasks with a scheduled thread pool and ensure proper shutdown of the thread pool when the application context is closed. The number of threads in the pool is set to 100, but we can adjust this value based on the application’s requirements.

@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(100);
	}
}

3. Task Scheduler Configuration

By default, Spring uses a single-threaded task scheduler to run the tasks. So if we have multiple @Scheduled methods, each task needs to wait for the thread to complete executing a previous task.

3.1. Configuring Thread Pool Size

To enable executing multiple tasks, in parallel, we need to configure the ThreadPoolTaskScheduler bean:

@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {

  ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
  threadPoolTaskScheduler.setPoolSize(5);
  threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
  return threadPoolTaskScheduler;
}

In Spring Boot, the autoconfiguration makes it even easier. We can configure the above thread pool task executor using a simple property:

spring.task.scheduling.pool.size=5

3.2. Configuring Multiple Task Schedulers

Starting since Spring Framework 6.1 (and Spring Boot 3.2), the @Scheduled annotation supports a new attribute ‘scheduler‘ that can be used to specify which task scheduler to use to run the scheduled task when multiple schedulers are available.

@Scheduled(fixedRate = 5000, scheduler = "myScheduler")
public void doSomething() {

	// do some work
}

We can define the schedulers in any @Configuration class as regular beans.

@Configuration
@EnableScheduling
public class ExplicitSchedulerConfig {

	String threadName;

	@Bean @Qualifier("myScheduler")
	public TaskScheduler taskScheduler1() {
	
		ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
		scheduler.setThreadNamePrefix("explicitScheduler1");
		return scheduler;
	}

	@Bean
	public TaskScheduler taskScheduler2() {

		ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
		scheduler.setThreadNamePrefix("explicitScheduler2");
		return scheduler;
	}
}

This can be extremely beneficial when we want to run some scheduled tasks in virtual threads using VirtualThreadTaskExecutor and other tasks on platform threads.

4. Using @Scheduled Annotation

After the task scheduling has been enabled, we can add the @Scheduled annotation to a method, along with trigger metadata. Internally, ScheduledAnnotationBeanPostProcessor that will be created by the imported SchedulingConfiguration scans all declared beans for the presence of the @Scheduled annotations.

  • The @Scheduled methods must have ‘void‘ returns and must not accept any arguments.
  • The other beans required by the scheduled method must be provided using dependency injection.
  • The @Scheduled is a repeatable annotation. If several @Scheduled declarations are found on the same method, each of them will be processed independently, with a separate trigger firing for each of them. In this case, be careful to avoid possible overlapping executions.
  • Do not use @Configurable annotation on bean classes containing @Scheduled methods. It will cause double initialization and each @Scheduled method will be invoked twice.

In the following example, the doSomething() method is invoked every five seconds with a fixed delay.

@Scheduled(fixedDelay = 5000)
public void doSomething() {

	// method is invoked every five seconds
}

It is important to know that the delay period is measured from the completion time of each preceding invocation. It means that once the doSomething() method is executed, the countdown for the next execution begins not from the start time of the previous execution, but from the time when the previous execution completes.

Note that, by default, milliseconds will be used as the time unit for fixed delay, fixed rate, and initial delay values. We can change the unit using the timeUnit attribute. For example, the doSomething() method will be executed every 24 hours.

@Scheduled(fixedDelay = 24, timeUnit = TimeUnit.HOURS)
public void doSomething() {

  // method is invoked every five seconds
}

5. Schedule a One-time Task

For one-time tasks, we can just specify an initial delay by indicating the amount of time to wait before the intended execution of the method.

In the following example, the doSomething() will run only once after the application has been started and with an initial delay of 10 seconds.

@Scheduled(initialDelay = 10000)
public void doSomething() {
	// something that should run only once
}

6. Schedule a Task at Fixed Rate (Concurrent Executions Allowed)

Fixed-rate execution does not wait for the previous execution to finish and can execute the same method in two threads if the previous execution is still running.

@Scheduled(fixedRate = 10000)
public void run() {

  //...
}

Note that, by default, Spring does not allow to run the same method in multiple threads. To enable this behavior completely, we must configure the @Async support also.

@Configuration
@EnableScheduling
@EnableAsync
public class SchedulerConfig {

  ///...
}

Then use the @Async method on the scheduled method.

@Scheduled(fixedRate = 10000)
@Async
public void taskWithConcurrentExecutions() {

  //...
}

7. Schedule a Task at Fixed Delay (Concurrent Executions NOT Allowed)

Execute a task at a fixed interval of time. It makes sure that the previous method execution is finished before it starts executing the method again after the configured fixed delay.

@Scheduled(fixedDelay = 10000)
public void run() {

  //...
}

8. Using CRON Expressions

The @Scheduled annotation is very flexible and may accept the cron expression as well.

@Scheduled(cron = "0 10 10 10 * ?")
public void run() {
	
   //...
}

The corn expressions are hard for humans to parse and are, therefore, hard to fix in case of bugs. To improve readability, Spring supports the following macros, which represent commonly used sequences.

  • @yearly (or @annually): once a year
  • @monthly: once a month
  • @weekly: once a week
  • @daily (or @midnight): once a day
  • @hourly: once an hour
@Scheduled(cron = "@hourly")
public void run() {
	
   //...
}

9. @Scheduled Annotation on Reactive Methods

As of Spring Framework 6.1, @Scheduled methods are also supported on reactive methods with a Publisher return type (or any concrete implementation of Publisher such as Mono and Flux).

These methods must be declared without any arguments.

@Scheduled(fixedDelay = 5000)
public Mono<Void> reactiveSomething() {

	// return an instance of Publisher
}
  • If a reactive method emits values (onNext signal), they are ignored.
  • If an error occurs during execution, it’s logged but doesn’t stop the scheduled tasks.

In the following example, the method returns Flux that emits values. These values are completely ignored between subsequent executions of the method.

@Scheduled(fixedDelay = 5000)
public Flux<String> reactiveSomething() {
	return Flux.just("Hello", "World");
}

When we destroy the annotated bean or close the application context, scheduled tasks are canceled. This includes canceling the next scheduled subscription and any ongoing subscriptions.

10. Conclusion

In this Spring @Scheduled example, we learned to enable task scheduling and configure the repeated task executions with initial delay, fixed delay and fixed rate. We saw how to use the @Scheduled annotation and how to use @Async annotation to configure the concurrent task executions.

Drop me your questions on this spring task scheduler annotation example.

Happy Learning !!

Source Code on Github

Comments

Subscribe
Notify of
guest
16 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.