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.
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.