@Async
是 Spring 框架提供的用于异步执行方法的注解,使用时需要注意以下几个关键点,以确保正确实现异步功能并避免常见问题:
1.首先要在springBoot项目内创建线程池配置类:
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* 线程池配置
*
* @author jsdye
**/
@Configuration
public class ThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = 50;
// 最大可创建的线程数
private int maxPoolSize = 200;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build());
}
}
有了这个线程池,就可以直接在异步方法上使用@Async注解(
当 Spring 容器中只存在一个 TaskExecutor 或 ThreadPoolTaskExecutor 类型的 Bean(即你配置的 threadPoolTaskExecutor)时
Spring 会自动将它作为所有 @Async 方法的默认线程池(即使不显式指定名称),若存在多个TaskExecutor 或 ThreadPoolTaskExecutor 类型的 Bean,就需要显示指定用哪一个Bean,例如:
@Async("threadPoolTaskExecutor")
public void saveOrEditBoxInfo(String deviceCode, String jsonString) {
//业务代码
}
);
;若没有这种线程池,那么使用单独@Async注解,就会很消耗性能(@Async注解默认
使用 SimpleAsyncTaskExecutor异步执行器(每次调用新建线程,导致线程不可复用,不推荐生产环境使用))
2. 开启异步支持
在 Spring Boot 应用中,需要在主类或配置类上添加 @EnableAsync
注解:
@SpringBootApplication
@EnableAsync // 启用异步方法支持
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 方法必须是 public 且非静态
@Async
只能用于 public
方法,且不能是 static
方法。因为 Spring 通过 AOP(面向切面编程)代理实现异步,而私有 / 静态方法无法被代理。
4. 避免方法内部调用
同一个类中的方法调用带 @Async
的方法会失效,因为 AOP 代理是通过外部调用触发的。例如:
public class MyService {
public void doSomething() {
// 错误:this 指向当前对象,而非代理对象
this.asyncMethod();
}
@Async
public void asyncMethod() {
// 异步执行的代码
}
}
解决方案:将异步方法提取到另一个 Bean 中;
或通过依赖注入获取当前 Bean 的代理对象例子如下:
@Service
public class MyService {
@Autowired
private MyService self; // 注入当前Bean的代理对象
@Async("threadPoolTaskExecutor")
public CompletableFuture<String> asyncMethod() {
System.out.println("执行线程: " + Thread.currentThread().getName());
return CompletableFuture.completedFuture("异步结果");
}
public void syncMethod() {
System.out.println("主线程: " + Thread.currentThread().getName());
self.asyncMethod(); // ✅ 通过代理对象调用,异步生效
}
}
5. 返回值类型限制
- 无返回值:方法应声明为
void
。 - 有返回值:必须返回
CompletableFuture
、Future
或其子类,并通过AsyncResult
包装结果:
@Async
public CompletableFuture<String> asyncWithResult() {
// 异步处理
return CompletableFuture.completedFuture("结果");
}
若返回值类型不匹配(如直接返回 String
),会抛出异常。
6. 异常处理
- 无返回值方法:异常会被
AsyncUncaughtExceptionHandler
捕获,需自定义处理器: -
@Configuration public class AsyncConfig implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); } }
有返回值方法:异常会被封装在
Future
中,需通过future.get()
捕获。
7. 事务与异步的冲突
异步方法与事务管理(如 @Transactional
)结合时需谨慎,因为两者使用不同的 AOP 代理机制。例如:
- 异步方法中开启的事务可能无法正确传播。
- 建议将事务操作放在同步方法中,异步方法仅处理非事务逻辑。
8. 序列化问题
若异步方法在分布式环境中跨进程调用(如通过消息队列),参数和返回值必须可序列化(实现 Serializable
接口)。
9. 测试异步方法
单元测试中,异步方法可能不会立即执行。可使用 @EnableAsync
和 @Async
在测试类中模拟异步行为,或通过 CompletableFuture.get()
等待结果。
总结
合理使用 @Async
能显著提升应用性能,但需注意代理机制、线程池配置和异常处理等细节。建议结合自定义线程池和完善的监控机制,确保异步任务的稳定性。