回答
在 Spring 中,@Async
用来标记一个方法为异步执行。当某个在某个方法上面使用 @Async
来标注,那么当应用执行该方法时,可能会以异步的方式执行该方法。
那为什么不推荐直接使用它呢?这是因为 Spring 使用 TaskExecutor
作为异步方法执行的底层机制,@Async
注解会告诉 Spring 将该方法的执行放入一个线程池中,而线程池是由 TaskExecutor
提供,如果我们直接使用 @Async
,不手动配置线程池,则 Spring 会使用默认的 SimpleAsyncTaskExecutor
,该线程池会在每次调用时创建一个新的线程,而不是复用线程池中的线程,这就会导致大量线程会被创建,导致资源浪费。
详解
SimpleAsyncTaskExecutor
是 Spring 提供的一个 TaskExecutor
实现,它的主要特点是每次调用时都会创建一个新的线程,而不是使用线程池:
public void execute(Runnable task, long startTimeout) {
Assert.notNull(task, "Runnable must not be null");
Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);
if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
this.concurrencyThrottle.beforeAccess();
doExecute(new ConcurrencyThrottlingRunnable(taskToUse));
}
else {
doExecute(taskToUse);
}
}
protected void doExecute(Runnable task) {
// 新建线程
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
thread.start();
}
每次新建线程会使得 SimpleAsyncTaskExecutor
变得非常轻量级,适合在需要快速实现异步任务的小任务,但是在高负载或者并发量较大的场景下就不适应了。当然,在实际生产过程中不推荐使用它,我们应该配置自定义的 TaskExecutor
:
@EnableAsync
@Configuration
public class TaskPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
// 返回可用处理器的Java虚拟机的数量 12
int i = Runtime.getRuntime().availableProcessors();
System.out.println("系统最大线程数 : " + i);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程池大小
executor.setCorePoolSize(16);
// 最大线程数
executor.setMaxPoolSize(20);
// 配置队列容量,默认值为Integer.MAX_VALUE
executor.setQueueCapacity(99999);
// 活跃时间
executor.setKeepAliveSeconds(60);
// 线程名字前缀
executor.setThreadNamePrefix("asyncServiceExecutor -");
// 设置此执行程序应该在关闭时阻止的最大秒数,以便在容器的其余部分继续关闭之前等待剩余的任务完成他们的执行
executor.setAwaitTerminationSeconds(60);
// 等待所有的任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
然后在使用 @Async
时使用它即可: