Introduction to Spring Boot Scheduler

In this tutorials of Spring Boot, we will look at the Spring boot scheduler. We will see how to schedule tasks with Spring Boot. During this post, let’s look at the Spring @Scheduled annotation.

 

Introudction

Spring Boot use the @Scheduled annotation to schedule tasks. It internally use the TaskScheduler interface for scheduling the annotated methods for execution. While using this annotation, we may need to follow certain rules:

  1. Methos should not accept any parameters.
  2. Return type for the method should be void.

 

1. Project Setup.

Let’s create a simple application for our Spring boot scheduler. We have the following options to create a Spring Boot project.

  1. Use Spring Initializr
  2. IDE to create project structure

This is how our pom.xml look like:

<?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.1.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javadevjournal</groupId>
	<artifactId>spring-boot-scheduler</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-scheduler</name>
	<description>Spring Boot schedule sample application</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>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

 

2. Enable Scheduling 

T0 enable the scheduling; we need to add the @EnableScheduling annotation. We have the following 2 options to add this annotation in our application:

  1. Add the @EnableScheduling annotation to the main class.
  2. Annotate the configuration class with this annotation.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringBootSchedulerApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootSchedulerApplication.class, args);
	}
}

[pullquote align=”normal”]The @EnableScheduling annotation creates a background task executor. [/pullquote]

 

3. Scheduling Tasks

The main work for any scheduler is to schedule the tasks. Spring Boot make it easy to create a scheduling task. We only need to annotate the method with the @Scheduled annotation. Let’s look at one of the example for a better understanding: 

@Component
public class SayHelloTask {

    private static final Logger LOG = LoggerFactory.getLogger(SayHelloTask.class);

    @Scheduled(fixedRate = 1000)
    public void sayHello(){
        LOG.info("Hello from our simple scheduled method");
    }
}

Let’s look at some important points:

  1. The @Scheduled annotation defines the scheduling (e.g. when method will run etc.)
  2. We can pass some parameters to the annotation for customize the behaviour.

If we run this application, you will see the following output in the console after application startup:

2019-10-10 20:53:12.447  INFO 45786 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method
2019-10-10 20:53:13.448  INFO 45786 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method
2019-10-10 20:53:14.446  INFO 45786 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method
2019-10-10 20:53:15.450  INFO 45786 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method
2019-10-10 20:53:16.448  INFO 45786 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method
2019-10-10 20:53:17.446  INFO 45786 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method

In the next section, we will look at some parameter which can be used with the Scheduled annotation.

 

4. Tasks with Fixed Rate

To schedule a method trigger at a fixed internal, we can use the fixedRate parameter in the @Scheduled annotation. Let’s take an example, where we want to execute method every 1 seconds:

@Scheduled(fixedRate = 1000)
    public void sayHello(){
        LOG.info("Hello from our simple scheduled method");
}

 

5.  Scheduling  with Fixed Delay

Let’s say we want a fixed delay between the last execution and start of next execution. We can use the fixedDelay parameter in this annotation. This parameter counts the delay after the completion of the last invocation.

@Scheduled(fixedDelay = 2000)
public void fixedDelayExample(){
  LOG.info("Hello from our Fixed delay method");
}

This is how the output is look like:

2019-10-10 21:19:38.331  INFO 46159 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method
2019-10-10 21:19:40.333  INFO 46159 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method
2019-10-10 21:19:42.345  INFO 46159 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method
2019-10-10 21:19:44.346  INFO 46159 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method

Here, the tasks getting triggered with the delay of 2 seconds. Let’s add some change to our method for a better understanding. Let’s assume that, our tasks takes 3 minutes to complete, in this case, next execution should start in 5 seconds (3 seconds for completion and 2 seconds delay).

@Scheduled(fixedDelay = 2000)
public void fixedDelayExample() {
    LOG.info("Hello from our Fixed delay method");
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException ie) {
        LOG.error("Got Interrupted {}", ie);
    }
}

When we run this code, we will have following output:

2019-10-10 21:25:11.623  INFO 46242 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method
2019-10-10 21:25:16.629  INFO 46242 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method
2019-10-10 21:25:21.633  INFO 46242 --- [  scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our Fixed delay method

Total delay of 5 seconds.

 

5.1.  Parallel Scheduling

We can also enable parallel scheduling by adding the @Async annotation to the scheduled tasks. Let’s look at this example:

@EnableAsync
public class ParallelSchedulingExample {
    @Async
    @Scheduled(fixedDelay = 2000)
    public void fixedDelayExample() {
        LOG.info("Hello from our Fixed delay method");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException ie) {
            LOG.error("Got Interrupted {}", ie);
        }
    }
}

 

6. Schedule a Task with Initial Delay

We can also use the initialDelay parameter to delay the first execution of the task with the specified number of milliseconds.We can combine this parameter with fixedRate and fixedDelay based on our requirements.

@Scheduled(fixedRate = 2000, initialDelay = 5000)
public void scheduleTaskWithInitialDelay() {
    LOG.info("Fixed Rate Task with Initial Delay");
}

@Scheduled(fixedRate = 2000, fixedDelay = 5000)
public void scheduleTaskWithInitialDelay() {
    LOG.info("Fixed Rate Task with Initial Delay");
}

When we run our application, we will have following output:

2019-10-10 21:58:01.415  INFO 46959 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Fixed Rate Task with Initial Delay
2019-10-10 21:58:03.412  INFO 46959 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Fixed Rate Task with Initial Delay
2019-10-10 21:58:05.417  INFO 46959 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Fixed Rate Task with Initial Delay
2019-10-10 21:58:07.415  INFO 46959 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Fixed Rate Task with Initial Delay

 

7. Cron Expression

Cron expression is a flexible and powerful way to schedule the tasks. The pattern is a list of six single space-separated fields: representing second, minute, hour, day, month, weekday. Month and weekday names can be given as the first three letters of the English names. In this example, we are scheduling the tasks for every 1 minute using cron expression:

@Scheduled(cron = "0 * * * * ?")
 public void scheduleTaskWithCronExpression() {
     LOG.info("Example to show how cron expression can be used");
 }

When we run our application, it will execute the tasks in every 1 minute. This is how the output look like:

2019-10-12 19:25:00.003  INFO 74830 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Example to show how cron expression can be used
2019-10-12 19:26:00.003  INFO 74830 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Example to show how cron expression can be used

 

8. Parametrising the Schedule

Spring Boot and Spring provides a powerful mechanism to externalize your configuration using the properties file. While working on any enterprise applications, It’s always good practice to externalize the configuration to avoid hard coding. It also helps in following aspects

  1. Ability to change the configurations without redeploy.
  2. System do not need any re-compile for property changes.

Let’s use the Spring expression language to externalize the schedule expressions through property file.This is how the new code look like:

@Scheduled($ {
    fixedrate.value
})
public void sayHello() {
    LOG.info("Hello from our simple scheduled method");
}

@Scheduled($ {
    fixeddelay.value
})
public void fixedDelayExample() {
    LOG.info("Hello from our Fixed delay method");
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException ie) {
        LOG.error("Got Interrupted {}", ie);
    }
}

@Scheduled($ {
    cron.expression
})
public void scheduleTaskWithCronExpression() {
    LOG.info("Example to show how cron expression can be used");
}

 

9. Custom Thread Pool

The @Scheduled annotation will execute the tasks in the default thread pool.Spring creates the default thread pool of the size 1 during the startup.Let’s run our previous examples to verify this statement:

2019-10-13 11:23:13.224  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1
2019-10-13 11:23:14.225  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1
2019-10-13 11:23:15.225  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1
2019-10-13 11:23:16.225  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1
2019-10-13 11:23:17.224  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1
2019-10-13 11:23:18.221  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1
2019-10-13 11:23:19.225  INFO 88646 --- [   scheduling-1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: scheduling-1

We added the following line in the log to print thread name: Thread.currentThread().getName(). Spring provides the flexibility to create custom thread pool and execute all the tasks using the custom thread pool. Let’s see how we can create and configure custom thread pool for our application. 

@Configuration
public class CustomThreadPoolConfig implements SchedulingConfigurer {

    private final int CUSTOM_POOL_SIZE = 5;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {

        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(CUSTOM_POOL_SIZE);
        threadPoolTaskScheduler.setThreadNamePrefix("javadevjournal");
        threadPoolTaskScheduler.initialize();

        //let's register our custom thread pool scheduler
        scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }
}

Now when we run our application, Spring will use the new thread pool.This is how the output look like:

2019-10-13 11:32:54.570  INFO 88821 --- [javadevjournal1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal1
2019-10-13 11:32:55.571  INFO 88821 --- [javadevjournal2] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal2
2019-10-13 11:32:56.571  INFO 88821 --- [javadevjournal1] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal1
2019-10-13 11:32:57.571  INFO 88821 --- [javadevjournal3] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal3
2019-10-13 11:32:58.572  INFO 88821 --- [javadevjournal3] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal3
2019-10-13 11:32:59.571  INFO 88821 --- [javadevjournal3] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal3
2019-10-13 11:33:00.569  INFO 88821 --- [javadevjournal3] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal3
2019-10-13 11:33:01.572  INFO 88821 --- [javadevjournal3] c.j.schedule.task.SayHelloTask           : Hello from our simple scheduled method, current thread is :: javadevjournal3</code?

[pullquote align=”normal”] Spring scheduling is a good alternate for simple use cases but if you are looking for more advance scheduling framework (like persistence etc.), consider using Quartz scheduler. [/pullquote]

 

Summary

In this post, we looked at the Spring boot scheduler. we understood the way to configure and use the @Scheduled annotation. We saw different option to customize the @Scheduled annotation by passing different parameters. In the end of this post, we saw how to configure custom thread pool for our application. The source code for this post is available on the GitHub

2 thoughts on “Introduction to Spring Boot Scheduler”

  1. Can you please explain How to get status of a task from default Scheduler? I want to schedule few task and monitor it which ome is running currently and finished ? Thank you.

Comments are closed.