说起异步编程,不管是哪种语言都是程序员们绕不开的难题。毕竟在真实业务场景中,多线程处理技术至关重要,今天我们来聊聊SpringBoot支持的异步注解@Async。
1.@Async用法
想要使用@Async异步注解,首先我们就必须要用到@EnableAsync注解,在SpringBoot启动类中设置打开异步注解功能。 @EnableAsync(proxyTargetClass = true),然后你就可以在项目中任意的需要异步的方法上加上@Async了。
2.弊端
但是。。。事情往往并没有那么简单。如果没有在 @Async 注解中指定线程池,就会使用默认的线程池。默认的线程池为 SimpleAsyncTaskExecutor,该线程池不会复用线程,每有一个新任务被提交,该线程池就会创建一个新的线程实例用于执行任务。下面为相关的代码:
protected void doExecute(Runnable task) {
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
thread.start();
}
3.解决方案
当我们看到new Thread()的时候就会恍然大悟,所以可见他的效率依旧很低,而且当数据量达到一定程度的时候,极有可能造成系统不可预估的状况。 而如果想要指定线程池,可以通过在 @Async 注解中的 value 参数中指定所要使用的线程池的 Bean Name 。另一种方法是是一个实现了 AsyncConfigurer 接口或是继承其默认适配器类 AsyncConfigurerSupport 的配置类,这样 @Async 注解的方法就会使用指定的自定义的线程池。

// ThredPoolTaskExcutor的处理流程
// 当池子大小小于corePoolSize,就新建线程,并处理请求
// 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去workQueue中取任务并处理
// 当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理
// 当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁
// 当threadNamePrefix设置为true,则核心线程也会超时关闭
其中还采用了线程池拒绝策略,线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。
4.测试

当起1000个线程时,明显可以看到配置了线程池的cpu占用率更低。