Spring Scheduler

In this article, we will understand how to schedule tasks in Spring so that they are executed repeatedly using @Scheduled annotation with example programs.

Spring boot scheduling
Scheduling means executing tasks repeatedly at a fixed time and/or interval automatically. To enable scheduling in a Spring boot application, below configuration is required

1. @EnableSchduling annotation
Apply @EnableScheduling annotation over main class of the application or any @Configuration class as shown below

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

@SpringBootApplication
@EnableScheduling
public class DatamigrationApplication {


}

2. @Scheduled annotation
Apply @Scheduled annotation over the method that needs to scheduled. This method shall contain the task to be executed repeatedly.

Methods with @Scheduled annotation should fulfill following two conditions:

I. It should not accept any arguments.
If method with @Scheduled is provided with arguments, the application throws following error at startup

java.lang.IllegalStateException: Encountered invalid @Scheduled method ‘repeat’: Only no-arg methods may be annotated with @Scheduled

II. It should not return any value
Return type of @Scheduled method should be void, that is, it should not return any value.
Even if the method returns a value, this will not be any error, but the value returned will be ignored.

@Scheduled options
Spring boot supports following options for scheduling tasks
1. At fixed rate
Tasks are executed at fixed time interval. Tasks keep on executing at given interval and there is no relation between previous and next tasks.
So, with fixed rate of 2 seconds, tasks will keep on executing every 2 seconds even if each task takes 5 seconds.

This is done by providing fixedRate attribute followed by the interval in milliseconds with @Scheduled.
Example,

@Scheduled(fixedRate = 5000)
void repeat() {
  try {
    LocalDateTime now = LocalDateTime.now();
    System.out.println("Repeated at: " + now.getHour() + ":" + now.getMinute() + ":" + now.getSecond());
    // sleep for 10 seconds
    Thread.sleep(10000);
    System.out.println("---- Task completed ----");
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

Here, sleep() is used to represent a task that takes 10 seconds.
Look at the below output to understand the working of fixedRate.

Repeated at: 15:1:16
—- Task completed —-
Repeated at: 15:1:26
—- Task completed —-
Repeated at: 15:1:36
—- Task completed —-
Repeated at: 15:1:46
—- Task completed —-
Repeated at: 15:1:56
—- Task completed —-
Repeated at: 15:2:6
—- Task completed —-
Repeated at: 15:2:16

You can also provide a string value to fixedRate. But, it should be parseable to numeric format. Example, fixedRate = "5000".

Parallel Scheduling with fixedRate
Note that even the method is scheduled with fix rate of 5 seconds and the task takes 1o seconds, the next task did not start after 5 seconds.
This is because by default tasks do not run in parallel.
To enable parallel scheduling, add @EnableAsync annotation over the class and @Async annotation over the method with @Scheduled as below.

@EnableScheduling
@EnableAsync
public class SchedulerExample {
  @Scheduled(fixedRate = 5000) 
  void repeat() { 
    try { 
      LocalDateTime now = LocalDateTime.now(); 
      System.out.println("Repeated at: " + now.getHour() + ":" + now.getMinute() + ":" + now.getSecond()); 
      // sleep for 10 seconds Thread.sleep(10000); 
      System.out.println("---- Task completed ----"); 
    } catch (InterruptedException e) { 
      e.printStackTrace(); 
    } 
  }
}

Output now will change to

Repeated at: 15:13:14
Repeated at: 15:13:19
—- Task completed —-
Repeated at: 15:13:24
—- Task completed —-
Repeated at: 15:13:29
Repeated at: 15:13:34
—- Task completed —-
Repeated at: 15:13:39
—- Task completed —-
Repeated at: 15:13:44
—- Task completed —-

Note that new task is started after every 5 seconds(fixedRate interval) irrespective of the status of current executing task.

2. At fixed delay
Tasks are executed with a delay of given time interval between them.
With fixed delay of 5 seconds, a task will start 5 seconds after the current executing task completes, irrespective of the time taken by the executing task.
Fixed delay is configured using fixedDelay attribute with @Scheduled. Example,

@Scheduled(fixedDelay = 5000)
public void repeat() {
  try {
    LocalDateTime now = LocalDateTime.now();
    System.out.println("Repeated at: " + now.getHour() + ":" + now.getMinute() + ":" + now.getSecond());
    Thread.sleep(10000);
    System.out.println("---- Task completed ----");
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

Look at the output below to understand fixedDelay

Repeated at: 15:18:54
—- Task completed —-
Repeated at: 15:19:9
—- Task completed —-
Repeated at: 15:19:24
—- Task completed —-
Repeated at: 15:19:39
—- Task completed —-
Repeated at: 15:19:54
—- Task completed —-
Repeated at: 15:20:9

That is, with a fixed delay of 5 seconds, if the current executing task completes at 10:30 AM, next task will begin at 10:35 AM.
And, if this task takes 10 seconds, then the next task will begin at 10:45 AM and so on.

Thus, with fixedDelay, new task will start at

Time at which the current executing task completes + delay time

Scheduling with a delay
Suppose you want to schedule tasks starting after some interval. @Scheduled provides an initialDelay attribute with both fixedRate and fixedDelay, which is applicable for the first task. Example,
@Scheduled(fixedRate = 1000, initialDelay = 2000)
This will start the first task after 2 seconds. After that, all tasks will execute at fixed interval of 1 seconds.
3. Cron expression
A cron expression is very powerful and can be used to schedule tasks at particular time or even at some date, month or year etc.
Cron expression is a UNIX format cron expression consisting of 6 value types or fields as shown below
Scheduling tasks with cron expressionwhere, possible values in each field are:
Second, Minute: May be a value between 0 and 59.
Hour: May be a value between 0 and 23.
Day of Month: A value between 1 and 31.
Month: A value between 1 and 12 or Names such as JAN, FEB etc.
Day of Week: Value between 0 and 7 or Names such as SUN, MON etc., where 0 and 7 stand for SUN.

Following special characters are supported in a CRON expression
* : All possible values in a field.
– : Range of values. Example, 9-11 in hour field means execute from 9 AM to 11 AM.
, : Specify multiple values. Example, 9,10,11 in hour field means execute from 9, 10 and 11 AM.
/ : Specifying interval or step size. Example, */10 in seconds field means execute a task every 10 seconds.
? : Can be used in only day of month or day of week fields. It is used as a substitute to ignore the field value. Example, to execute a task on 20th of every month, specify 20 in day of month field and ? in day of week field since we do not care about the day.

Cron expression examples
Below is a list of some cron expressions and their meanings.

  • * * * * * *
    Execute at every second.
  • 0 * * * * *
    At first second of every minute.
  • 0 0 * * * *
    Execute at first second of first minute of every hour. That is, as 00:00:00, 01:00:00, 02:00:00 etc.
  • */10 * * * * *
    Execute at every ten seconds.
  • * */5 * * * *
    Execute every 5 minutes.
  • 0 0 8-10 * * *
    8, 9 and 10 o’clock of every day.
  • 0 0 6,19 * * *
    6:00 AM and 7:00 PM every day.
  • 0 0/30 8-10 * * *
    8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 AM every day.
  • 0 0 9-17 * * MON-FRI
    Every hour nine-to-five weekdays.
  • 0 0 0 25 12 ?
    Every Christmas Day at midnight or 00:00.
  • 0 0 8 1 1 MON, TUE
    Execute a task on 1 Jan at 8 AM, if it falls on Monday or Tuesday.

You can sense that a cron expression is much more powerful that fixedRate and fixedDelay attributes.
Example of scheduling with cron expression is

@Scheduled(cron = "* 0 * * * *")
public void repeat1() {
  try {
    LocalDateTime now = LocalDateTime.now();
    System.out.println("Repeated at: " + now.getHour() + ":" + now.getMinute() + ":" + now.getSecond());
    System.out.println("---- Task completed ----");
  } catch (Exception e) {
    e.printStackTrace();
  }
}

Above method will execute at first second of every minute as evident from the below output

Repeated at: 20:57:0
—- Task completed —-
Repeated at: 20:58:0
—- Task completed —-
Repeated at: 20:59:0
—- Task completed —-
Repeated at: 21:0:0
—- Task completed —-

Default time zone taken by cron is the system time zone. To specify a different time zone, use zone attribute with cron as
@Scheduled(cron = "* * * * * *", zone="America/Los_Angeles"

Passing schedule time from properties
It is a better practice to control schedule time from properties file instead of writing them in code. This can be done as

@Scheduled(fixedRate="${fixed.rate.value}")

@Scheduled(fixedDelay="${fixed.delay.value}")

@Scheduled(cron="${cron.exp}")

and providing these properties in application.properties file of your application as shown below.

fixed.rate.value=2000
fixed.delay.value=1000
cron.exp=0 * * * * *

That is all on scheduling tasks in Spring boot with @Scheduled annotations at fixed rate, fixed delay and cron expresssions with example programs.
Hope the article was useful.