Spring Boot Handbook

    Task Scheduling in Spring Boot

    Introduction#

    Tasks are scheduled in Spring Boot for the execution of specified tasks or methods, either once or repeatedly, at certain times or intervals, without human intervention. The feature is vital for automating background processes and optimizing application performance.

    Spring Boot supports task scheduling through the **@Scheduled**annotation along with the TaskScheduler interface. Using this support, the execution of tasks can be scheduled easily.

    Typical Applications of Task Scheduling#

    • Log user activity on a separate thread.
    • Send notifications (emails/SMS) asynchronously after some user actions.
    • Perform parallel processing of image uploads, database queries, or API calls.
    • Clean up expired records from the database.
    • Archive logs at prescribed rates
    • Fetch data from external APIs at specified intervals
    • Carry out health checks on external systems or services.

    @Scheduled Annotation#

    This annotation performs the task of running processes at given intervals or times.

    How It Works:#

    1. Enable Scheduling

    In the configuration class, you have to specify @EnableScheduling to turn on the scheduling service.

    Enable Scheduling image
    1. Annotate the Methods

    It applies the @Scheduled annotation to any method that is to be scheduled. The method must satisfy:

    • Has no return type (void)
    • No parameters are passed to it (no arguments)
     @Scheduled Annotate the Methods

    Key parameters of @Scheduled#

    This annotation,@Scheduled, makes it possible for Spring Boot to define any given task to be scheduled using a variety of timing options:

    fixedRate The defined interval between the start of two successive executions is not affected by the duration of the task.

    @Scheduled(fixedRate = 5000) // Runs every 5 seconds public void executeTask() { System.out.println("Task executed at: " + LocalDateTime.now()); }

    Output:

    fixedRate : Runs every 5 seconds

    fixedDelay The amount of time the task is to wait before being scheduled again from the end of the last execution.

    @Scheduled(fixedDelay = 5000) // Runs 5 seconds after the last task completes public void executeTask() { System.out.println("Task executed at: " + LocalDateTime.now()); }

    Output:

    fixedDelay:Runs 5 seconds after the last task completes

    initialDelay The first execution of the task is delayed (using this property) and then continued according to either fixedRate or fixedDelay.

    @Scheduled(fixedRate = 5000, initialDelay = 2000) // Starts after 2 seconds, then every 5 seconds public void executeTask() { System.out.println("Task executed at: " + LocalDateTime.now()); }

    Output

    initialDelay: Starts after 2 seconds, then every 5 seconds

    cron It uses a cron expression to have complex scheduling patterns (e.g., daily, weekly, or specific times).

    cron expression
    @Scheduled(cron = "0 0/5 * * * ?") // Runs every 5 minutes public void executeTask() { System.out.println("Task executed at: " + LocalDateTime.now()); }

    Output

    cron: Runs every 5 minutes

    How @Scheduled works Inside Implementation?#

    When a method is annotated with @Scheduled, the following actions are performed by Spring:

    • Register the method as scheduled tasks.
    @Scheduled(fixedRate = 1000) // NOT concurrent void logMe() { log.info("Scheduler1 started... {}", Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } log.info("Scheduler1 ended... {}", Thread.currentThread().getName()); }
    • Delegate the task execution to an implementing class of the TaskScheduler interface.
    • Lastly, schedule them by a thread pool (the default one is ScheduledExecutorService).
    import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; import java.util.concurrent.*; @SpringBootApplication @Slf4j @EnableScheduling public class LearnAsyncSchedulingApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(LearnAsyncSchedulingApplication.class, args); } @Override public void run(String... args) throws Exception { ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(6, new ThreadFactory() { @Override public Thread newThread(Runnable r) { System.out.println(" "); return new Thread(r, "thread "+System.nanoTime()); } }); System.out.println("Starting main thread " + Thread.currentThread().getName()); } }

    Output

    task execution

    Default Behavior#

    @Scheduled runs the tasks on a single-threaded executor by default. If you schedule more than one task, they execute one after the other sequentially, not in parallel.

    How to enable the concurrent execution#

    To make them run simultaneously, define a ThreadPoolTaskScheduler to be a custom TaskScheduler for the thread pool.

    Execution Flow#

    • @Scheduled uses Spring's TaskScheduler to get that working.
    • TaskScheduler will now be a ThreadPoolTaskScheduler (Spring's default implementation) by default.
    • ThreadPoolTaskScheduler internally delegates their executions easily to the ScheduledExecutorService.

    TaskScheduler#

    An interface in the Spring scheduling framework that is capable of scheduling tasks dynamically at specific intervals or point-in-time will be TaskScheduler.

    Essential characteristics:#

    • Dynamic Scheduling: Scheduling tasks programmatically with fixed rate, fixed delay, or cron specifications.
    • Granular Control: Create, cancel, or reschedule a task from anywhere in the application.
    • Cron Expression Support: Allows precise scheduling based on cron expressions.
    • Thread Pooling: Integrates with**ThreadPoolTaskScheduler** to perform tasks in parallel rather than more efficiently.

    Configuring TaskScheduler#

    • Spring Boot makes an out-of-the-box provided default TaskScheduler through ThreadPoolTaskScheduler, where this uses Java's ScheduledExecutorService.
    • To allow concurrent executions, define a new ThreadPoolTaskScheduler bean with a configurable thread pool size.
    import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @Configuration public class ThreadConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(4); taskScheduler.setThreadNamePrefix("CustomTask-"); taskScheduler.initialize(); return taskScheduler; } }
    • Tasks can be scheduled dynamically by using TaskScheduler.schedule(), which supports fixed delay, fixed rate, and cron expression.

    Static Scheduling (@Scheduled)

    import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component @Slf4j public class MyScheduler { @Scheduled(fixedRate = 200)// NOT concurrent void logMe() { log.info("Scheduler1 started... {}", Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }

    Dynamic Scheduling (TaskScheduler.schedule())

    import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.EnableScheduling; import java.time.Instant; @SpringBootApplication @Slf4j @EnableScheduling public class LearnAsyncSchedulingApplication implements CommandLineRunner { @Autowired private TaskScheduler taskScheduler; public static void main(String[] args) { SpringApplication.run(LearnAsyncSchedulingApplication.class, args); } @Override public void run(String... args) throws Exception { taskScheduler.schedule(() -> { log.info("Running after 2 sec"); }, Instant.ofEpochSecond(2)); } }

    Output#

    1. Output Without Dynamic Scheduling (@Scheduled)
      • The task runs automatically at a fixed rate.
      • It uses the default single-threaded TaskScheduler, meaning tasks run sequentially.
      • No manual control over task execution.
    Output Without Dynamic Scheduling 
    1. Output With Dynamic Scheduling (TaskScheduler.schedule())
      • The task executes only once dynamically at the scheduled time.
      • It does not repeat automatically like @Scheduled.
      • Allows more flexibility (e.g., run at specific user-defined times).
      • Uses a custom thread pool, enabling parallel execution.
    Output With Dynamic Scheduling

    Key Benefits:#

    • Supports concurrent execution through multiple threads.
    • Supports dynamic scheduling, rescheduling, and canceling of tasks.
    • It is much faster than the default single-threaded scheduler.

    Final Comparison: Static vs. Dynamic Scheduling#

    Feature@Scheduled (Static)TaskScheduler.schedule() (Dynamic)
    Execution TypeFixed intervalManually scheduled
    RepetitionYes, automatically repeatsNo, executes once unless rescheduled
    ThreadingDefault single-threadedUses a thread pool (if configured)
    Use CasePeriodic tasks like log rotation or auto-backupsDynamic tasks like delayed processing or user-scheduled jobs

    Conclusion#

    This article elaborated on task scheduling in Spring Boot through the use of @Scheduled and TaskSchedulerto perform task automation at specified intervals or otherwise. It also contains the important methods in scheduling, how to configure ThreadPoolTaskScheduler for concurrent execution, and real-life use cases such as notification logging, data cleanup, and API polling, among others.

    Last updated on Apr 09, 2025