异步处理框架@Async在SpringBoot中的使用

@Async异步处理框架
分析
在SpringBoot的日常开发中,一般都是==同步==调用的,但经常有特殊业务需要做==异步==来处理。比如:注册用户、需要送积分、发短信和邮件、或者下单成功、发送消息等等。
优势
- 第一个原因:容错问题,如果送积分出现异常,不能因为送积分而导致用户注册失败。
- 第二个原因:提升性能,比如注册用户花了30毫秒,送积分划分50毫秒,如果同步的话一共耗时:70毫秒,用异步的话,无需等待积分,故耗时是:30毫秒就完成了业务。
同步执行和异步执行
同步执行(串行执行)
==代码顺序执行==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
@RestController @Slf4j public class RegController { @Autowired private RegService regService;
@GetMapping("/reg") public R register() { log.info("用户注册"); regService.sendMsg(); log.info("发送短信"); regService.addScore(); return R.success("OK"); } }
|

问题:
串行执行的时长:是所有方法执行的总和。
打个比方:
用户注册:50MS
短信发送:100ms
添加积分:100ms
总时长:250ms
完毕
异步执行

问题:异步执行的时长:是最后一个方法执行完成的时间。
打个比方:
用户注册:50MS
短信发送:100ms
添加积分:100ms
总时长:100ms
完毕。
使用
开启异步执行
| @SpringBootApplication @EnableAsync public class StudyBootsProApplication { public static void main(String[] args) { SpringApplication.run(StudyBootsProApplication.class, args); } }
|
定义异步处理的service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
@Service @Slf4j public class RegService {
@Async public void sendMsg(){ try{ Thread.sleep(5000); log.info("------------发送短信--------------"); }catch (Exception e){ e.printStackTrace(); } } @Async public void addScore(){ try{ Thread.sleep(3000); log.info("-------处理积分-------"); }catch (Exception e){ e.printStackTrace(); }
} }
|
调用异步处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
@RestController @Slf4j public class RegController { @Autowired private RegService regService;
@GetMapping("/reg") public R register() { log.info("用户注册"); regService.sendMsg(); log.info("发送短信"); regService.addScore(); return R.success("OK"); } }
|
运行结果
| 2021-08-30 20:48:00.990 INFO 8348 --- [nio-8081-exec-1] com.ajie.controller.RegController : 用户注册 2021-08-30 20:48:00.996 INFO 8348 --- [nio-8081-exec-1] com.ajie.controller.RegController : 发送短信 2021-08-30 20:48:04.018 INFO 8348 --- [-async-thread-2] com.ajie.sevice.RegService : -------处理积分------- 2021-08-30 20:48:06.016 INFO 8348 --- [-async-thread-1] com.ajie.sevice.RegService : ------------发送短信--------------
|
异步线程池的优化
==Springboot的tomcat的线程默认数量:200个,如果异步线程线程过多,有请求线程、异步处理的线程这个时候,这么线程都在争抢CPU的执行时间。这样很耗费资源 ,因为@Async](https://github.com/Async)注解默认情况下用的是`SimpleAsyncTaskExecutor`线程池.[该线程池不是真正意义上的线程】==
因为线程不重用,每次调用都会新建一个新的线程。
通过上面的日志分析获得结论:【task-1】,【task-2】,【task-3】….递增。
@Async注解异步框架提供多种线程机制:
- SimpleAsyncTaskExecutor:简单的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
- SyncTaskExecutor:这个类没实现异步调用,只是一个同步操作,只适合用于不需要多线程的地方。
- ConcurrentTaskExecutor:Executor的适配类,不推荐使用.。
- ThreadPoolTaskScheduler:可以和cron表达式使用。
- ThreadPoolTaskExecutor:最常用,推荐,其本质就是:java.util.concurrent.ThreadPoolExecutor的包装
配置SyncThreadPoolConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
@Configuration public class SyncThreadPoolConfiguration {
@Bean(name = "thrandPoolTaskExecutor") public ThreadPoolTaskExecutor getThreadPoolTaskExecutor(){ ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(100);
threadPoolTaskExecutor.setQueueCapacity(200);
threadPoolTaskExecutor.setKeepAliveSeconds(200); threadPoolTaskExecutor.setThreadNamePrefix("ajie-async-thread-");
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor; } }
|
使用场景
请注意:异步虽好但是不能泛滥使用。大部分开发中,还是串行执行。
除非在开发过程中,一个业务和另外一个业务的关联性不是强耦合,执行失败或者成功都不影响它核心业务。你可以把这些附属业务剥离处理用异步执行。
比如:用户注册:发送短信,发送邮件, 比如:下单成功发送短信,发送微信登等
异步编程的框架:消息中间件(ActiveMQ、RabbitMQ)