Spring Boot — How to Schedule Tasks | Code Factory

Code Factory
6 min readJun 16, 2020

Donate : Link

WordPress Blog : Link

You’ll learn how to schedule tasks in Spring Boot using @Scheduled annotation. You’ll also learn how to use a custom thread pool for executing all the scheduled tasks.

The @Scheduled annotation is added to a method along with some information about when to execute it, and Spring Boot takes care of the rest.

Spring Boot internally uses the TaskScheduler interface for scheduling the annotated methods for execution.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot-scheduler-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-scheduler-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

You can enable scheduling simply by adding the @EnableScheduling annotation to the main application class or one of the Configuration classes.

Open SpringBootSchedulerDemoApplication.java and add @EnableScheduling annotation like so -

package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SpringBootSchedulerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSchedulerDemoApplication.class, args);
}
}

Scheduling a task with Spring Boot is as simple as annotating a method with @Scheduled annotation, and providing few parameters that will be used to decide the time at which the task will run.

Before adding tasks, Let’s first create the container for all the scheduled tasks. Create a new class called ScheduledTasks inside com.example.schedule package with the following contents -

package com.example.schedule;import java.time.format.DateTimeFormatter;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class ScheduledTasks {
private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);
private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
public void scheduleTaskWithFixedRate() {} public void scheduleTaskWithFixedDelay() {} public void scheduleTaskWithInitialDelay() {} public void scheduleTaskWithCronExpression() {}
}

The class contains four empty methods. We’ll look at the implementation of all the methods one by one.

All the scheduled methods should follow the following two criteria -

  • The method should have a void return type.
  • The method should not accept any arguments.

1. Scheduling a Task with Fixed Rate

You can schedule a method to be executed at a fixed interval by using fixedRate parameter in the @Scheduled annotation. In the following example, The annotated method will be executed every 2 seconds.

@Scheduled(fixedRate = 2000)
public void scheduleTaskWithFixedRate() {
logger.info("Fixed Rate Schedule : " + dateTimeFormatter.format(LocalDateTime.now()));
}

Output :

Fixed Rate Schedule : 14:33:40
Fixed Rate Schedule : 14:33:42
Fixed Rate Schedule : 14:33:44
Fixed Rate Schedule : 14:33:46
...
...

The fixedRate task is invoked at the specified interval even if the previous invocation of the task is not finished.

2. Scheduling a Task with Fixed Delay

You can execute a task with a fixed delay between the completion of the last invocation and the start of the next, using fixedDelay parameter.

The fixedDelay parameter counts the delay after the completion of the last invocation.

Consider the following example -

@Scheduled(fixedDelay = 2000)
public void scheduleTaskWithFixedDelay() {
logger.info("Fixed Delay Schedule : " + dateTimeFormatter.format(LocalDateTime.now()));
try {
TimeUnit.SECONDS.sleep(5);
} catch(InterruptedException e) {
e.printStackTrace();
}
}

Output :

Fixed Delay Schedule : 14:35:58
Fixed Delay Schedule : 14:36:05
Fixed Delay Schedule : 14:36:12
Fixed Delay Schedule : 14:36:19
...
...

Since the task itself takes 5 seconds to complete and we have specified a delay of 2 seconds between the completion of the last invocation and the start of the next, there will be a delay of 7 seconds between each invocation

3. Scheduling a Task With Fixed Rate and Initial Delay

You can use initialDelay parameter with fixedRate and fixedDelay to delay the first execution of the task with the specified number of milliseconds.

In the following example, the first execution of the task will be delayed by 5 seconds and then it will be executed normally at a fixed interval of 2 seconds -

@Scheduled(fixedRate = 2000, initialDelay = 5000)
public void scheduleTaskWithInitialDelay() {
logger.info("Fixed Rate Task with Initial Delay Schedule : " + dateTimeFormatter.format(LocalDateTime.now()));
}

Output :

Fixed Rate Task with Initial Delay Schedule : 14:39:10
Fixed Rate Task with Initial Delay Schedule : 14:39:12
Fixed Rate Task with Initial Delay Schedule : 14:39:14
Fixed Rate Task with Initial Delay Schedule : 14:39:16
...
...

4. Scheduling a Task using Cron Expression

If the above simple parameters can not fulfill your needs, then you can use cron expressions to schedule the execution of your tasks.

In the following example, I have scheduled the task to be executed every minute -

@Scheduled(cron = "0 * * * * ?")
public void scheduleTaskWithCronExpression() {
logger.info("Cron task Schedule : " + dateTimeFormatter.format(LocalDateTime.now()));
}

Output :

Cron task Schedule : 14:42:00
Cron task Schedule : 14:43:00
Cron task Schedule : 14:44:00
...
...

Running @Scheduled Tasks in a Custom Thread Pool

By default, all the @Scheduled tasks are executed in a default thread pool of size one created by Spring.

You can verify that by logging the name of the current thread in all the methods -

logger.info("Current Thread : {}", Thread.currentThread().getName());

Output :

Current Thread : scheduling-1

But hey, You can create your own thread pool and configure Spring to use that thread pool for executing all the scheduled tasks.

Create a new package config inside com.example, and then create a new class called SchedulerConfig inside config package with the following contents -

package com.example.config;import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}

That’s all you need to do for configuring Spring to use your own thread pool instead of the default one.

If you log the name of the current thread in the scheduled methods now, you’ll get the output like so -

Current Thread : my-scheduled-task-pool-2
Current Thread : my-scheduled-task-pool-1
Current Thread : my-scheduled-task-pool-4
...

Final Output :

Current Thread : my-scheduled-task-pool-1
Fixed Rate Task with Initial Delay Schedule : 14:50:52
Current Thread : my-scheduled-task-pool-4
Fixed Rate Schedule : 14:50:53
Current Thread : my-scheduled-task-pool-3
Fixed Rate Task with Initial Delay Schedule : 14:50:54
Current Thread : my-scheduled-task-pool-6
Fixed Rate Schedule : 14:50:55
Current Thread : my-scheduled-task-pool-7
Fixed Rate Task with Initial Delay Schedule : 14:50:56
Current Thread : my-scheduled-task-pool-2
Fixed Rate Schedule : 14:50:57
Current Thread : my-scheduled-task-pool-5
Fixed Rate Task with Initial Delay Schedule : 14:50:58
Current Thread : my-scheduled-task-pool-9
Fixed Delay Schedule : 14:50:58
Current Thread : my-scheduled-task-pool-8
Fixed Rate Schedule : 14:50:59
Current Thread : my-scheduled-task-pool-10
Cron task Schedule : 14:51:00
Current Thread : my-scheduled-task-pool-4
Fixed Rate Task with Initial Delay Schedule : 14:51:00
Current Thread : my-scheduled-task-pool-3
Fixed Rate Schedule : 14:51:01
Current Thread : my-scheduled-task-pool-6
Fixed Rate Task with Initial Delay Schedule : 14:51:02
Current Thread : my-scheduled-task-pool-7
Fixed Rate Schedule : 14:51:03
Current Thread : my-scheduled-task-pool-2
Fixed Rate Task with Initial Delay Schedule : 14:51:04
Current Thread : my-scheduled-task-pool-1
Fixed Rate Schedule : 14:51:05
Current Thread : my-scheduled-task-pool-5
Fixed Delay Schedule : 14:51:05
Current Thread : my-scheduled-task-pool-9
Fixed Rate Task with Initial Delay Schedule : 14:51:06
Current Thread : my-scheduled-task-pool-10
...
...

--

--