@Async
是Spring Framework提供的一个注解,用于在Spring应用程序中异步执行方法。当需要执行一个耗时的任务,而不想阻塞主线程时,这个方法就非常有用。以下是如何使用@Async
以及一些注意事项:
如何使用@Async
启用异步支持
在启动类上添加@EnableAsync
注解,以启用异步方法执行的能力。
@EnableAsync
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
定义异步方法
在任何@Component
或@Service
注解的类中,使用@Async
注解声明一个异步方法。
@Service
public class AsyncService {
@Async(executor = "taskExecutor")
public void executeAsyncTask() {
// 执行耗时操作的代码
}
@Async(executor = "taskExecutor")
// 异步方法通常不返回结果,因为调用者不会等待结果。如果需要返回结果,可以使用Future或CompletableFuture。
public CompletableFuture<ResultType> executeAsyncTaskWithResult() {
// 执行耗时操作并返回结果
}
}
调用异步方法
可以像调用普通方法一样调用异步方法,Spring 会在一个单独的线程中执行该方法。
@Service
public class SomeService {
@Resource
private AsyncService asyncService;
public void someMethod() {
asyncService.executeAsyncTask();
// 这个方法会立即返回,executeAsyncTask() 在另一个线程中执行。
}
}
配置异步执行器(可选)
可以自定义用于执行异步方法的Executor
,如果不配置,Spring默认使用一个简单的Executor
。
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置线程池的基本线程数(核心池大小),即即使当前没有任务执行,线程池也会保持10个线程处于活跃状态。
executor.setCorePoolSize(10);
// 设置线程池中允许的最大线程数。如果任务数量超过核心池大小,线程池会创建新的线程来处理任务,直到达到这个最大值。
executor.setMaxPoolSize(20);
// 设置等待执行的任务队列的容量。如果线程池中的线程都处于活跃状态,新提交的任务将被放入这个队列中等待执行。
executor.setQueueCapacity(100);
// 初始化线程池执行器。在Spring的上下文中,通常不需要显式调用initialize()方法,因为Spring会自动初始化Bean。但在这里调用可以确保在返回执行器之前,执行器已经被正确地设置和初始化。
executor.initialize();
return executor;
}
如果希望所有标记了<font style="color:rgb(6, 6, 7);">@Async</font>
的方法都使用同一个<font style="color:rgb(6, 6, 7);">taskExecutor</font>
,可以在配置类中设置默认的执行器。
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurationSupport {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置线程池的基本线程数(核心池大小),即即使当前没有任务执行,线程池也会保持10个线程处于活跃状态。
executor.setCorePoolSize(10);
// 设置线程池中允许的最大线程数。如果任务数量超过核心池大小,线程池会创建新的线程来处理任务,直到达到这个最大值。
executor.setMaxPoolSize(20);
// 设置等待执行的任务队列的容量。如果线程池中的线程都处于活跃状态,新提交的任务将被放入这个队列中等待执行。
executor.setQueueCapacity(100);
// 初始化线程池执行器。在Spring的上下文中,通常不需要显式调用initialize()方法,因为Spring会自动初始化Bean。但在这里调用可以确保在返回执行器之前,执行器已经被正确地设置和初始化。
executor.initialize();
return executor;
}
@Override
public Executor getAsyncExecutor() {
return taskExecutor();
}
}
注意事项
@Async
注解的方法必须在Spring管理的Bean中调用。- 异步方法不能用于构造函数、
@PostConstruct
方法或@PreDestroy
方法。 - 异步方法抛出的异常不会传播给调用者,需要在异步方法内部处理异常。
- ****异步方法不支持当前的事务传播行为,因为它们在不同的线程中执行。
- 如果异步方法需要返回值,应使用
Future
或CompletableFuture
作为返回类型。 - ****确保异步方法中使用的共享资源是线程安全的。
- 如果不配置自定义的
Executor
,Spring将使用默认的SimpleAsyncTaskExecutor
,这可能会导致线程创建开销。 - ****异步方法可能会使调试和日志记录变得复杂,因为它们在不同的线程中执行。
- 考虑实现错误处理机制,比如使用
@Async
注解的noResultException
属性来指定当异步执行出现异常时应该抛出的异常类型。