Scheduled Tasks in Spring with @Scheduled

Learn How to write Scheduled Tasks Using @Scheduled Annotation in Spring.

Overview

Often, we need to write Scheduled Tasks or Scheduled Methods that is executed as per the given schedule. And, such scheduled tasks have a variety of use cases including a scheduled batch processing, or a nightly job that fetches data every night. The scheduled methods are executed asynchronously while the other parts of the application continue to work independently.

In this tutorial we will see how can we use Spring to easily write scheduled methods. We will also cover the four ways of Scheduling tasks using Spring.

Spring @Scheduled Annotation

Spring Framework has excellent support to the method scheduling. Using the @Scheduled annotation, we can convert almost any method in the application to a Scheduled Method.

The @Scheduled is a method level annotation and we need to add it on a method of a Spring Bean class. We can provide parameters to the annotations to specify if we wish the method to be executed on a fixed interval or at a specific schedule of time and date.

Enable Scheduling in Spring Application

By default, the Scheduled processing is disabled in a. Spring Application. In order to Enable scheduling in Spring Application we need to add @EnableScheduling annotation on a Configuration.

Next is an example of the Application class in our Spring Boot project. Notice that the class has the @EnableScheduling annotation.

package com.amitph.spring.tutorials.springbootdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}Code language: Java (java)

Or, next is an example of Enabling Scheduling using Spring XML Configuration.

<task:annotation-driven>Code language: HTML, XML (xml)

When the @EnableScheduling annotation or the annotation-driven tag is detected, Spring scans the application packages to find all the Spring Beans having @Scheduled methods and set up their execution schedule.

Now, we have done everything that is required to add Scheduled Methods in Spring Boot Application. We can schedule a method at a fixed delay, a fixed rate or on a specific cron schedule. Before we move ahead let’s understand Fixed Delay vs Fixed Rate.

Difference Between Fixed Delay and Fixed Rate Scheduling

As stated earlier, in Spring we use @Scheduled annotation on a method to schedule its execution. While doing so we can use properties like fixedDelay or fixedRate to specify a number of milliseconds.

When a fixedDelay is specified, the next execution will only begin a specified number of milliseconds after the previous execution is finished. On the other hand, when fixedRate is used, the next execution will begin a specified number of milliseconds after the start of previous one.

The fixedDelay should be used, when the executions are dependent on each other and we always we wish the next execution to start only a certain while after the first finishes.

However, the fixedRate should be used when the rate of of the execution matters more and it is irrespective of the time each execution takes to finish.

Scheduled Task with Fixed Delay

See the example to Schedule a task to run after a fixed delay of 2 seconds.

@Scheduled(fixedDelay = 2000)
public void taskWithFixedDelay() {
    logger.info("Task with Fixed Delay, " + (LocalTime.now().getSecond()));
}Code language: Java (java)

Spring executes the above method at a fixed delay of 2 seconds. Which means, the duration between the end of first execution and the start of the next will always be 2 seconds. In other words, the next execution won’t start until the specific fixed delay is elapsed after the completion of the current execution.

Scheduled Task at Fixed Rate

Next, is an example of of scheduling a method at a fixed interval.

@Scheduled(fixedRate = 2000)
public void taskWithFixedRate() throws InterruptedException {
    logger.info("Task with Fixed Rate, " + (LocalTime.now().getSecond()));
    MILLISECONDS.sleep(1000L);
}Code language: Java (java)

When we set fixedRate value, the next execution will be triggered when the given number of milliseconds are elapsed from the beginning of the current execution. That is why, when we run the above, we can see the method is executed at a fixed interval of 2 seconds, irrespective of the sleep time of 1 second.

However, it is important to know that scheduling cannot run two executions concurrently. Which means, if the sleep time exceeds the specified fixed rate value, the next execution will wait until the previous execution is finished.

Allow Concurrent Executions of Scheduled Tasks

If we wish to allow concurrent execution of these scheduled methods we can do by used @EnableAsync and @Async annotations.

@Component
@EnableAsync
public class ScheduledTask {
    private final Logger logger = LoggerFactory.getLogger(ScheduledTask.class);

    @Scheduled(fixedRate = 2000)
    @Async
    public void taskWithFixedRate() throws InterruptedException {
        logger.info("Task with Fixed Rate, " + (LocalTime.now().getSecond()));
        MILLISECONDS.sleep(4000L);
    }
}Code language: Java (java)

In the above example, the sleep time is more than the fixed interval. However, as we have enabled the asynchronous execution, the method will be executed concurrently, at the fixed rate of 2 seconds.

Scheduled Tasks with Initial Delay

We can schedule a task having initial delay by using initialDelay property.

@Scheduled(fixedRate = 3000, initialDelay = 10000)
public void taskWithFixedRateAndInitialDelay(){
    logger.info("Task with Fixed Rate and Initial Delay, " + (LocalTime.now().getSecond()));
}Code language: Java (java)

We can use initialDelay along with both fixedRate and fixedDelay type of scheduling. When initialDelay is set, the first execution of the method won’t start until the specified number of milliseconds are passed. Also, the initialDelay as no impact on the subsequent executions of the task.

Scheduled Task using Cron Expressions

We can use cron property to specify a cron expression to define the execution schedule of a method. Using cron expression we can provide a more complex, a more flexible schedule for the tasks.

@Scheduled(cron = "0 45 1,2,3 * * ?", zone = "Europe/London")
public void taskWithCronExpression(){
    logger.info("Task with Cron Expression, " + (LocalTime.now().getSecond()));
}Code language: Java (java)

Here, we have scheduled our method to execute it on 1:45 AM, 2:45 AM, and 3:45 AM everyday. We can also specify the optional field of zone to provide a specific timezone. The scheduler will execute this method once the given time is reached in London.

Reading the Schedule from Properties File

So far, we have hard coded the Task Schedules. However, we can also keep these schedules in a properties file and refer them using Spring Expressions.

Next, is an example of a properties file

schedule.fixedDelay=2000
schedule.fixedRate=2000
schedule.initialDelay=10000
schedule.cron=0 45 1,2,3 * * ?
schedule.timezone=Europe/LondonCode language: Properties (properties)

We can refer to these properties using Spring Expressions and shown next.

package com.amitph.spring.tutorials.springbootdemo.scheduled;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalTime;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

@Component
@EnableAsync
public class ScheduledTask {
    private final Logger logger = LoggerFactory.getLogger(ScheduledTask.class);

    @Scheduled(fixedDelayString = "${schedule.fixedDelay}")
    public void taskWithFixedDelay() {
        logger.info("Task with Fixed Delay, " + (LocalTime.now().getSecond()));
    }

    @Scheduled(fixedRateString = "${schedule.fixedRate}")
    @Async
    public void taskWithFixedRate() throws InterruptedException {
        logger.info("Task with Fixed Rate, " + (LocalTime.now().getSecond()));
        MILLISECONDS.sleep(4000L);
    }

    @Scheduled(fixedRateString = "${schedule.fixedRate}", initialDelayString = "${schedule.initialDelay}")
    public void taskWithFixedRateAndInitialDelay() {
        logger.info("Task with Fixed Rate and Initial Delay, " + (LocalTime.now().getSecond()));
    }

    @Scheduled(cron = "${schedule.cron}", zone = "${schedule.timezone}")
    public void taskWithCronExpression() {
        logger.info("Task with Cron Expression, " + (LocalTime.now().getSecond()));
    }
}Code language: Java (java)

Note that, the fixedRate, fixedDelay, and initialDelay properties are numeric. However, the Spring expression substitution happens as String. That is why, we are using the fixedRateString, fixedDelayString, and initialDelayString variants of these properties respectively.

Summary

In this tutorial we learned How to Write Scheduled Tasks in Spring using @Scheduled annotation. We started by understanding that we need to enable Scheduling by using @EnableScheduling. Most importantly, we covered all four ways of Scheduling tasks in Spring. We have also referred to various Scheduled tasks in a Spring Boot Application.

To learn more about Spring and Spring boot please visit Introduction to Spring Framework and Spring Boot Introduction.

For full source code of the examples used here, please visit our Github Repository.