Backend Development 10 min read

Mastering Async and Scheduled Tasks in Spring Boot 3.2.5

This guide explains how to enable and use Spring Boot's @Async and @Scheduled annotations, configure various TaskExecutor and TaskScheduler implementations, leverage new Spring 6.1 pause/resume and virtual‑thread features, and apply graceful shutdown for both asynchronous and scheduled jobs.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Async and Scheduled Tasks in Spring Boot 3.2.5

1. Introduction

Spring provides powerful support for asynchronous and scheduled tasks. The @Async annotation runs a method in a separate thread and returns a Future or CompletableFuture to the caller. The @Scheduled annotation defines a timed task that can be configured via cron expressions or fixed rates, allowing efficient resource utilization.

2. Basic Usage

2.1 Asynchronous Task

<code>@EnableAsync
public class AppConfig {}

@Service
public class EmailService {
    @Async
    public void sendMail() {
        // TODO
    }
}</code>

Two steps enable async execution and annotate the method.

2.2 Scheduled Task

<code>@EnableScheduling
public class AppConfig {}

@Component
public class StatisticsService {
    // Executes every day at midnight
    @Scheduled(cron = "0 0 * * ?")
    public void calcDayAmount() {
        // TODO
    }
}</code>

Similarly, enable scheduling and annotate the method. Additional @Scheduled attributes (fixed delay, fixed rate, etc.) can be used.

3. TaskExecutor and TaskScheduler Implementations

Spring defines two core interfaces: TaskExecutor for async execution and TaskScheduler for scheduled execution. Common TaskExecutor implementations include:

SyncTaskExecutor – synchronous execution (mainly for testing).

SimpleAsyncTaskExecutor – creates a new thread per task, supports concurrency.

ConcurrentTaskExecutor – rarely used directly.

ThreadPoolTaskExecutor – most common; exposes ThreadPoolExecutor properties for bean‑based configuration.

Example bean configuration:

<code>@Bean
public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
    SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
    executor.setThreadNamePrefix("async-pack-");
    return executor;
}</code>

For scheduling, TaskScheduler implementations such as ThreadPoolTaskScheduler or ScheduledExecutorService can be customized:

<code>@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setPoolSize(2);
    scheduler.setThreadNamePrefix("scheduler-pack-");
    return scheduler;
}</code>

4. New Features in Spring 6.1

Starting with Spring 6.1, both ThreadPoolTaskExecutor and ThreadPoolTaskScheduler support pause/resume and graceful shutdown via the Spring lifecycle. Additionally, SimpleAsyncTaskExecutor and SimpleAsyncTaskScheduler introduce a virtualThreads option that aligns with JDK 21 virtual threads, providing lightweight concurrency and graceful shutdown.

5. Practical Example

5.1 Asynchronous Task Control

<code>@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(5);
    executor.setQueueCapacity(20);
    return executor;
}

@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;

@Async
public void async1() {
    System.err.printf("%s, async task async1 executing...%n", Thread.currentThread().getName());
}

public void stop() {
    this.threadPoolTaskExecutor.stop(() -> System.err.println("Async task stopped..."));
}

public void start() {
    this.threadPoolTaskExecutor.start();
}</code>

Calling /stop pauses new async tasks; /start resumes them. Existing running tasks continue until completion.

5.2 Scheduled Task Control

<code>@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setPoolSize(5);
    scheduler.setThreadNamePrefix("scheduler-pack-");
    return scheduler;
}

@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;

@Scheduled(fixedRate = 3000)
public void scheduler() {
    System.err.printf("%s, scheduled task executing...%n", Thread.currentThread().getName());
}

public void stop() {
    this.threadPoolTaskScheduler.stop(() -> System.err.println("Scheduled task stopped..."));
}

public void start() {
    this.threadPoolTaskScheduler.start();
}</code>

Stopping the scheduler pauses future executions; starting it resumes. Missed executions during the pause are executed once when resumed.

6. Graceful Shutdown

Both async and scheduled executors provide a shutdown method:

<code>ThreadPoolTaskExecutor#initiateShutdown();
ThreadPoolTaskScheduler#initiateShutdown();</code>

Invoking these methods triggers a graceful termination by calling the underlying thread pool’s shutdown() method.

JavaSpring BootAsyncscheduled tasksThreadPoolTaskExecutorThreadPoolTaskSchedulerSpring 6.1
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.