Do you think IIT Guwahati certified course can help you in your career?
No
Introduction
Scheduler in Spring Boot is a feature that allows you to run tasks at specific times or intervals without needing to manually trigger them. This is useful for automating tasks like sending emails, cleaning up old data, or performing regular database backups. With Spring Boot, setting up a scheduler is simple and efficient.
This article will discuss implementing schedulers in Spring Boot, explaining different scheduling techniques, their implementations, and how to parameterize schedules effectively.
Implementation
Spring Boot makes scheduling simple with the @Scheduled annotation. You need to enable scheduling and define when tasks should execute. Below, we’ll discuss the different ways to schedule tasks and how to implement them.
Enabling Support for Scheduling
To enable scheduling in your Spring Boot application, you must annotate your configuration class with @EnableScheduling. This ensures Spring’s task scheduler is initialized.
Example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerApplication.class, args);
}
}
Here, @EnableScheduling activates Spring’s scheduling capability. Once enabled, you can use the @Scheduled annotation to create scheduled tasks.
Scheduling Tasks Using a Cron Expression
Cron expressions are powerful tools to define complex schedules. They consist of six fields representing seconds, minutes, hours, day of the month, month, and day of the week.
Example:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class CronScheduler {
@Scheduled(cron = "0 0/5 * * * ?")
public void executeTask() {
System.out.println("Task executed at: " + java.time.LocalTime.now());
}
}
Explanation:
0 0/5 * * * ?: This cron expression means the task will run every 5 minutes.
@Scheduled: Marks the method for scheduling.
Output:
Every 5 minutes, the following will print:
Task executed at: HH:MM:SS
Scheduling Tasks at a Fixed Rate
A fixed rate scheduling ensures that tasks execute at regular intervals, regardless of the previous task’s completion.
Example:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class FixedRateScheduler {
@Scheduled(fixedRate = 5000)
public void executeFixedRateTask() {
System.out.println("Fixed rate task executed at: " + java.time.LocalTime.now());
}
}
Explanation:
fixedRate = 5000: The task runs every 5000 milliseconds (5 seconds).
Tasks will run at consistent intervals, regardless of their duration.
Output:
Every 5 seconds:
Fixed rate task executed at: HH:MM:SS
Scheduling Tasks to Run at a Fixed Delay
Fixed delay scheduling ensures that a task starts a fixed amount of time after the previous task finishes.
Example:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class FixedDelayScheduler {
@Scheduled(fixedDelay = 3000)
public void executeFixedDelayTask() {
System.out.println("Fixed delay task executed at: " + java.time.LocalTime.now());
}
}
Explanation:
fixedDelay = 3000: The task starts 3 seconds after the previous task completes.
Useful when tasks may vary in execution time.
Output:
After a 3-second delay from the last task completion:
Fixed delay task executed at: HH:MM:SS
Setting Delay or Rate Dynamically at Runtime
In some cases, you might need to change the delay or rate of a scheduled task while the application is running. For example, you may want to adjust the polling interval of an API based on external conditions. Spring Boot allows you to achieve this by using a `ScheduledTaskRegistrar` & custom logic to update the scheduling parameters dynamically.
Steps to Set Delay or Rate Dynamically
1. Create a Custom Scheduler Configuration
Use the `SchedulingConfigurer` interface to create a custom scheduler configuration.
2. Use `ScheduledTaskRegistrar` to Register Tasks
The `ScheduledTaskRegistrar` class allows you to programmatically register tasks with dynamic delays or rates.
3. Update the Delay or Rate at Runtime
Use a method to update the scheduling parameters dynamically.
For example:
Java Class (`DynamicSchedulerConfig.java`)
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.PeriodicTrigger;
import java.util.concurrent.TimeUnit;
@Configuration
public class DynamicSchedulerConfig implements SchedulingConfigurer {
private int initialDelay = 5000; // Initial delay of 5 seconds
private int updatedDelay = 10000; // Updated delay of 10 seconds
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
() -> System.out.println("Dynamic Task :: Execution Time - " + System.currentTimeMillis()),
triggerContext -> {
PeriodicTrigger trigger = new PeriodicTrigger(initialDelay, TimeUnit.MILLISECONDS);
return trigger.nextExecutionTime(triggerContext);
}
);
}
// Method to update the delay dynamically
public void updateDelay(int newDelay) {
this.initialDelay = newDelay;
System.out.println("Delay updated to: " + newDelay + " milliseconds");
}
}
Java Class (`DynamicSchedulerController.java`)
To demonstrate how to update the delay dynamically, let’s create a simple REST controller that allows us to change the delay at runtime.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DynamicSchedulerController {
@Autowired
private DynamicSchedulerConfig dynamicSchedulerConfig;
@GetMapping("/updateDelay")
public String updateDelay(@RequestParam int delay) {
dynamicSchedulerConfig.updateDelay(delay);
return "Delay updated to: " + delay + " milliseconds";
}
}
In this Code :
1. `DynamicSchedulerConfig`:
- Implements `SchedulingConfigurer` to configure tasks programmatically.
- Uses `ScheduledTaskRegistrar` to register a task with a dynamic delay.
- The `PeriodicTrigger` class is used to define the delay.
2. `DynamicSchedulerController`:
- Provides an endpoint (`/updateDelay`) to update the delay dynamically.
- Calls the `updateDelay` method in `DynamicSchedulerConfig` to change the delay.
3. How It Works:
- When the application starts, the task runs with an initial delay of 5 seconds.
- You can call the `/updateDelay` endpoint (e.g., `http://localhost:8080/updateDelay?delay=15000`) to change the delay to 15 seconds.
Key Points to Remember
1. Dynamic scheduling is useful when you need to adjust task intervals based on runtime conditions.
2. The `ScheduledTaskRegistrar` & `PeriodicTrigger` classes are key to implementing dynamic scheduling.
3. You can extend this approach to update rates or other scheduling parameters dynamically.
Running Tasks in Parallel
By default, Spring Boot runs scheduled tasks in a single-threaded manner. This means that if one task takes a long time to execute, it can delay other tasks. To avoid this, you can configure Spring Boot to run tasks in parallel using a thread pool. This allows multiple tasks to execute simultaneously, improving performance & responsiveness.
Steps to Run Tasks in Parallel
1. Enable Async Scheduling
Use the `@EnableAsync` annotation to enable asynchronous execution of tasks.
2. Configure a Thread Pool
Define a thread pool using the `TaskExecutor` interface or its implementations.
3. Annotate Scheduled Methods with `@Async`
Use the `@Async` annotation to mark methods that should run asynchronously.
Let’s take a complete example:
Java Class (`ParallelSchedulerConfig.java`)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync // Enable asynchronous execution
public class ParallelSchedulerConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // Set the core pool size
executor.setMaxPoolSize(10); // Set the maximum pool size
executor.setQueueCapacity(25); // Set the queue capacity
executor.setThreadNamePrefix("SchedulerThread-"); // Set thread name prefix
executor.initialize();
return executor;
}
}
Java Class (`ParallelScheduledTasks.java`)
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ParallelScheduledTasks {
@Async("taskExecutor") // Use the configured thread pool
@Scheduled(fixedRate = 2000) // Run every 2 seconds
public void taskOne() throws InterruptedException {
System.out.println("Task One :: Execution Time - " + System.currentTimeMillis() + " :: Thread - " + Thread.currentThread().getName());
Thread.sleep(3000); // Simulate a long-running task
}
@Async("taskExecutor") // Use the configured thread pool
@Scheduled(fixedRate = 2000) // Run every 2 seconds
public void taskTwo() throws InterruptedException {
System.out.println("Task Two :: Execution Time - " + System.currentTimeMillis() + " :: Thread - " + Thread.currentThread().getName());
Thread.sleep(3000); // Simulate a long-running task
}
}
In this Code:
1. `ParallelSchedulerConfig`:
Configures a thread pool using `ThreadPoolTaskExecutor`.
Sets the core pool size, maximum pool size, & queue capacity.
The `@EnableAsync` annotation enables asynchronous execution.
2. `ParallelScheduledTasks`:
Contains two scheduled tasks (`taskOne` & `taskTwo`) that run every 2 seconds.
The `@Async` annotation ensures that each task runs in a separate thread from the thread pool.
The `Thread.sleep(3000)` simulates a long-running task to demonstrate parallel execution.
3. How It Works:
Both tasks run every 2 seconds, but they don’t block each other because they execute in separate threads.
The thread pool ensures that multiple tasks can run simultaneously without delays.
Key Points to Remember
1. Running tasks in parallel improves performance, especially for long-running tasks.
2. Use the `@Async` annotation & configure a thread pool to enable parallel execution.
3. Be mindful of thread pool settings (core pool size, max pool size, & queue capacity) to avoid resource exhaustion.
Schedule a Task Using Cron Expressions
Cron expressions allow for more granular control over scheduling, including specific times or days. Here’s an example of scheduling a task to run every Monday at 10 AM.
Example:
@Component
public class WeeklyReportScheduler {
@Scheduled(cron = "0 0 10 * * MON")
public void generateWeeklyReport() {
System.out.println("Weekly report generated at: " + java.time.LocalTime.now());
}
}
Explanation:
0 0 10 * * MON: Executes the task at 10:00 AM every Monday.
Perfect for tasks like generating reports or backups.
Parameterizing the Schedule
Dynamic schedules allow developers to modify task timings without changing code. Use configuration files or databases to achieve this.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DynamicScheduler {
@Value("${scheduler.fixedRate}")
private long fixedRate;
@Value("${scheduler.fixedDelay}")
private long fixedDelay;
@Scheduled(fixedRateString = "${scheduler.fixedRate}")
public void fixedRateTask() {
System.out.println("Dynamic fixed rate task at: " + java.time.LocalTime.now());
}
@Scheduled(fixedDelayString = "${scheduler.fixedDelay}")
public void fixedDelayTask() {
System.out.println("Dynamic fixed delay task at: " + java.time.LocalTime.now());
}
}
Explanation:
The @Value annotation reads values from the application.properties file.
Task timings can be modified without changing the code.
Configuring Scheduled Tasks Using XML
While Spring Boot heavily relies on annotations for scheduling tasks, it also supports XML-based configuration for those who prefer it or need to maintain legacy code. XML configuration allows you to define scheduled tasks without using annotations like `@Scheduled`.
Steps to Configure Scheduled Tasks Using XML
1. Enable Scheduling in XML
First, you need to enable scheduling in your Spring configuration file (`applicationContext.xml` or any other XML file). This is done using the `<task:annotation-driven>` tag.
2. Define the Bean
Create a bean for the class that contains the scheduled tasks.
3. Configure the Task
Use the `<task:scheduled-tasks>` tag to define the tasks & their scheduling details.
Let’s take a complete example:
XML Configuration File (`applicationContext.xml`)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">
<!-- Enable scheduling -->
<task:annotation-driven/>
<!-- Define the bean -->
<bean id="scheduledTasks" class="com.example.ScheduledTasks"/>
<!-- Configure the task -->
<task:scheduled-tasks>
<!-- Run every 5 seconds -->
<task:scheduled ref="scheduledTasks" method="taskWithFixedRate" fixed-rate="5000"/>
<!-- Run 5 seconds after the previous task completes -->
<task:scheduled ref="scheduledTasks" method="taskWithFixedDelay" fixed-delay="5000"/>
</task:scheduled-tasks>
</beans>
Java Class (`ScheduledTasks.java`)
package com.example;
public class ScheduledTasks {
public void taskWithFixedRate() {
System.out.println("Fixed Rate Task (XML) :: Execution Time - " + System.currentTimeMillis());
}
public void taskWithFixedDelay() {
System.out.println("Fixed Delay Task (XML) :: Execution Time - " + System.currentTimeMillis());
}
}
In this Code
1. `<task:annotation-driven>`: Enables scheduling in the Spring application.
2. `<bean>`: Defines the bean for the `ScheduledTasks` class.
3. `<task:scheduled-tasks>`: Configures the tasks & their scheduling details.
- `ref`: Refers to the bean ID.
- `method`: Specifies the method to be executed.
- `fixed-rate` & `fixed-delay`: Define the scheduling intervals.
Key Points to Remember
1. XML configuration is useful when you want to avoid annotations or need to externalize configuration.
2. It’s a good practice to use XML for scheduling if you’re working with legacy systems or need to manage configurations outside the code.
3. You can mix XML-based & annotation-based scheduling in the same application.
Fixed Rate vs Fixed Delay
Parameters
Fixed Rate
Fixed Delay
Task Execution
Tasks are executed at a fixed interval, regardless of how long each task takes to complete.
The delay between task executions is fixed, meaning the next task starts after the specified delay.
Task Overlap
If a task takes longer than the specified interval, the next execution will start immediately after the current one finishes.
If a task takes longer than the specified delay, the next execution starts after the delay has elapsed.
Use Case
Suitable for tasks that need to run at strict intervals, such as polling or monitoring.
Suitable for tasks that require a certain cooldown period between executions, such as rate-limited API calls or resource-intensive operations.
Frequently Asked Questions
Can I use multiple @Scheduled annotations in a single class?
Yes, you can have multiple methods annotated with @Scheduled in the same class.
What happens if a task takes longer than the scheduled interval?
If using fixedRate, tasks may overlap. If using fixedDelay, the next task will wait until the previous one completes.
How do I test scheduled tasks?
You can test scheduled tasks using Spring’s TaskScheduler interface or mock the behavior in unit tests.
Conclusion
Schedulers in Spring Boot are an efficient way to automate tasks like sending notifications, generating reports, or running periodic jobs. By understanding cron expressions, fixed-rate, and fixed-delay scheduling, you can implement flexible and dynamic schedules in your applications.