使用 @Async 执行异步方法
学习使用 Spring 的 `@Async` 注解和线程池异步执行方法。
使用 @Async 执行异步方法 是 CoddyKit 上的免费 Spring Boot 4 Complete Guide 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Spring Boot 4 Complete Guide 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Spring Boot 4 Complete Guide 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Intro to Async Operations
Imagine you're ordering food online. If the app made you wait for the chef to cook your meal before you could even browse other options, that would be a bad experience!
This is like synchronous operations: one task must finish before the next can start. It blocks the main flow.
Asynchronous operations, on the other hand, let you start a task and immediately move on to something else. The task runs in the background, and you get notified when it's done.
- Non-blocking: Your application remains responsive.
- Efficient: Better utilization of resources by performing multiple tasks concurrently.
Sync vs. Async Demo
Let's see a simple Java example of a blocking (synchronous) operation. Notice how the main thread waits for doSyncTask() to complete.
Try running this example:
public class Main {
public static void main(String[] args) {
System.out.println("Main thread: Starting sync task...");
long startTime = System.currentTimeMillis();
doSyncTask(); // This call blocks the main thread
long endTime = System.currentTimeMillis();
System.out.println("Main thread: Sync task finished in " + (endTime - startTime) + "ms.");
System.out.println("Main thread: Continues immediately after sync task.");
}
public static void doSyncTask() {
try {
System.out.println("Sync task: Simulating work for 2 seconds...");
Thread.sleep(2000); // Simulate a long operation
System.out.println("Sync task: Completed!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Sync task: Interrupted!");
}
}
}Meet Spring's @Async
Spring Boot makes it incredibly simple to turn a synchronous method into an asynchronous one using the @Async annotation.
When you annotate a method with @Async:
- Spring executes that method in a separate thread.
- The calling method returns immediately, without waiting for the
@Asyncmethod to finish. - You don't have to manage threads manually!
Activate @Async
Before you can use @Async, you need to enable asynchronous processing in your Spring Boot application. This is done by adding the @EnableAsync annotation.
You typically place @EnableAsync on your main application class or a dedicated configuration class.
Here's how to add it:
package com.coddykit.async;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // This annotation activates @Async capability
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
System.out.println("Spring Boot application started with @EnableAsync!");
}
}@Async Method Basics
Once @EnableAsync is in place, you can simply add @Async to any public method in a Spring-managed component (like a @Service or @Component).
When performAsyncTask() is called, the main thread will print its next line immediately, while the async task runs in the background.
package com.coddykit.async;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
@Bean
public CommandLineRunner run(MyAsyncTaskService service) {
return args -> {
System.out.println("Main thread: Calling async task...");
service.performAsyncTask(); // This call returns immediately
System.out.println("Main thread: Async task called, continuing immediately.");
// Give the async task a chance to finish before the app exits
Thread.sleep(3000);
};
}
}
@Service
class MyAsyncTaskService {
@Async // Mark this method to run asynchronously
public void performAsyncTask() {
try {
System.out.println("Async thread: Starting long task...");
Thread.sleep(2000); // Simulate a long operation
System.out.println("Async thread: Long task completed!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Async thread: Task interrupted.");
}
}
}How @Async Works
When you use @Async, Spring doesn't create a new thread for every call. Instead, it uses a thread pool.
- A thread pool is a collection of pre-initialized threads that can execute tasks.
- When an
@Asyncmethod is called, Spring takes an available thread from the pool to execute it. - This avoids the overhead of creating new threads constantly and manages system resources efficiently.
- By default, Spring uses a
SimpleAsyncTaskExecutor, which can be inefficient for heavy loads.
Get Results with Future
What if your asynchronous method needs to return a result to the caller? You can't just return void or a direct value.
For this, you use java.util.concurrent.Future. The Future object acts as a placeholder for a result that isn't available yet.
- The
@Asyncmethod returnsFuture<T>(e.g.,Future<String>). - The calling code can later call
future.get()to retrieve the result (this call blocks until the result is ready). - You can also check
future.isDone()to see if the task has completed without blocking.
package com.coddykit.async;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Future;
import java.util.concurrent.AsyncResult;
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
@Bean
public CommandLineRunner run(MyFutureTaskService service) {
return args -> {
System.out.println("Main thread: Calling async task with Future...");
Future<String> futureResult = service.performFutureTask();
System.out.println("Main thread: Async task called, continuing immediately.");
// Main thread can do other work while async task runs
System.out.println("Main thread: Doing other work...");
Thread.sleep(500);
// Wait for the result (this loop checks, future.get() blocks)
while(!futureResult.isDone()) {
System.out.println("Main thread: Waiting for async result...");
Thread.sleep(500);
}
String result = futureResult.get(); // Retrieves the result (blocks if not done)
System.out.println("Main thread: Received result: " + result);
};
}
}
@Service
class MyFutureTaskService {
@Async
public Future<String> performFutureTask() {
try {
System.out.println("Async thread: Starting Future task...");
Thread.sleep(2000); // Simulate a long operation
System.out.println("Async thread: Future task completed!");
return new AsyncResult<>("Task finished successfully!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return new AsyncResult<>("Task interrupted!");
}
}
}Async Error Handling
When an @Async method (especially a void one) throws an exception, it's not propagated back to the calling thread by default. This means the exception might be silently swallowed, making debugging difficult.
To properly handle exceptions in @Async methods, you can implement AsyncConfigurer and provide a custom AsyncUncaughtExceptionHandler.
- This handler catches any exceptions thrown by
@Asyncmethods. - It allows you to log errors, send notifications, or perform other error recovery actions.
- For methods returning
Future, the exception is wrapped in theFutureand thrown whenfuture.get()is called.
Custom Thread Pool
For production applications, it's crucial to configure your own thread pool (Executor) for @Async. This gives you control over resources and performance.
You define a ThreadPoolTaskExecutor bean and specify its properties:
corePoolSize: The minimum number of threads to keep alive.maxPoolSize: The maximum number of threads the pool can contain.queueCapacity: The capacity of the queue for tasks waiting to be executed.threadNamePrefix: A prefix for the names of threads in this pool, useful for logging.
You can then specify which executor to use with @Async("executorBeanName").
package com.coddykit.async;
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
public class AsyncConfig {
@Bean(name = "customAsyncExecutor")
public Executor customAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2); // Keep 2 threads always running
executor.setMaxPoolSize(5); // Allow up to 5 threads if demand is high
executor.setQueueCapacity(10); // Queue up to 10 tasks if all threads are busy
executor.setThreadNamePrefix("CustomAsync-"); // Helps identify threads in logs
executor.initialize();
return executor;
}
// To use this, you'd annotate your async method like this:
// @Async("customAsyncExecutor")
// public void myCustomAsyncMethod() { /* ... */ }
}Async Quick Check
Let's check your understanding of Spring's @Async annotation.
Lesson Summary
Well done! You've learned the fundamentals of asynchronous processing with Spring Boot's @Async.
- We saw how synchronous operations block, while asynchronous operations allow non-blocking execution.
- The
@EnableAsyncannotation activates asynchronous method processing. - The
@Asyncannotation on a method ensures it runs in a separate thread. - For returning results from async methods, we use
Future<T>. - Custom
ThreadPoolTaskExecutorbeans allow fine-grained control over thread pool behavior.
Next, we'll dive into message queues, a powerful way to decouple services and handle asynchronous communication at a larger scale.
常见问题解答
「使用 @Async 执行异步方法」课时是免费的吗?
是的 — 「使用 @Async 执行异步方法」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Spring Boot 4 Complete Guide 课程的其余内容,请升级到 CoddyKit PRO。 Spring Boot 4 Complete Guide 课程共包含 4 节课。
「使用 @Async 执行异步方法」这节课中我会学到什么?
学习使用 Spring 的 `@Async` 注解和线程池异步执行方法。 你通过在浏览器中直接运行的动手代码来练习 Spring Boot 4 Complete Guide,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Spring Boot 4 Complete Guide 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Spring Boot 4 Complete Guide 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「使用 @Async 执行异步方法」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Spring Boot 4 Complete Guide 课中编写并运行代码吗?
能。每节 Spring Boot 4 Complete Guide 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 @Async 执行异步方法
- 消息队列简介
- 集成 RabbitMQ/Kafka
- 使用 @Scheduled 调度任务