Spring中使用@Async需启用支持、自定义线程池、方法public且非内部调用、返回Future或CompletableFuture,并注意上下文传递。

在 Spring 中使用 @Async 实现异步执行,核心是开启异步支持、配置线程池,并在方法上正确标注注解。不配置线程池时 Spring 会用默认的简单线程(无复用、无管理),生产环境必须自定义。
1. 启用 @Async 支持
在任意一个配置类或启动类上添加 @EnableAsync:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2. 定义可异步的方法
注意四点:
- 方法必须是 public 的(代理机制限制)
- 不能是同一个类内部的 self-invocation(即 A 方法调用本类的 B 方法,即使 B 加了
@Async也不会异步) - 返回值可以是
void或Future<t></t>(如CompletableFuture<string></string>更推荐) - 参数不能是原始类型(如
int),建议用包装类(Integer)或对象
示例:
立即学习“Java免费学习笔记(深入)”;
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
@Service
public class TaskService {
@Async
public CompletableFuture<String> sendEmail(String to) {
// 模拟耗时操作
try { Thread.sleep(2000); } catch (InterruptedException e) {}
return CompletableFuture.completedFuture("Email sent to " + to);
}
}
3. 自定义线程池(强烈推荐)
避免使用默认线程池,防止资源耗尽或任务堆积。配置一个命名、有界、带拒绝策略的线程池:
@Configuration
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-task-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
然后在方法上指定线程池名:
@Async("taskExecutor")
public CompletableFuture<Void> logOperation(String action) {
// ...
return CompletableFuture.completedFuture(null);
}
4. 调用与结果处理
异步方法立即返回,需主动获取结果或处理异常:
- 用
future.get()阻塞等待(慎用,可能引起线程阻塞) - 更推荐用
thenApply、exceptionally等非阻塞链式调用 - 若多个异步任务需组合,可用
CompletableFuture.allOf()或CompletableFuture.anyOf()
例如:
CompletableFuture<String> future = taskService.sendEmail("user@example.com");
future.thenAccept(result -> System.out.println(result))
.exceptionally(ex -> {
System.err.println("Failed: " + ex.getMessage());
return null;
});
不复杂但容易忽略:线程上下文(如 SecurityContext、RequestContextHolder)默认不会传递到异步线程,如有需要,得手动传播或配置 TaskDecorator。

















