SpringBoot异步使用@Async原理及线程池配置

前言

在实际项目开发中很多业务场景需要使用异步去完成,比如消息通知,日志记录,等非常常用的都可以通过异步去执行,提高效率,那么在Spring框架中应该如何去使用异步呢

使用步骤

完成异步操作一般有两种,消息队列MQ,和线程池处理ThreadPoolExecutor

而在Spring4中提供的对ThreadPoolExecutor封装的线程池ThreadPoolTaskExecutor,直接使用注解启用@Async,这个注解让我们在使用Spring完成异步操作变得非常方便

配置线程池类参数配置

自定义常量类

1public class ConstantFiledUtil { 2 public static final String AUTHORIZATION_TOKEN = "authorizationToken"; 3 /** 4 * kmall线程池名称 5 */ 6 public static final String KMALL_THREAD_POOL = "KmallThreadPool"; 7 8 /** 9 * kmall线程名称前缀 10 */ 11 public static final String KMALL_THREAD_NAME_PREFIX = "kmall-thread-"; 12} 13

配置线程池

1@Configuration(proxyBeanMethods = false) 2@EnableAsync //开启注解 3public class KmallConfig { 4 5 @Bean(ConstantFiledUtil.KMALL_THREAD_POOL) 6 public ThreadPoolTaskExecutor FebsShiroThreadPoolTaskExecutor() { 7 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 8 //配置核心线程数 9 executor.setCorePoolSize(5); 10 //配置最大线程数 11 executor.setMaxPoolSize(20); 12 //配置队列大小 13 executor.setQueueCapacity(200); 14 //线程池维护线程所允许的空闲时间 15 executor.setKeepAliveSeconds(30); 16 //配置线程池中的线程的名称前缀 17 executor.setThreadNamePrefix(ConstantFiledUtil.KMALL_THREAD_NAME_PREFIX); 18 //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean 19 executor.setWaitForTasksToCompleteOnShutdown(true); 20 //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住 21 executor.setAwaitTerminationSeconds(60); 22 // rejection-policy:当pool已经达到max size的时候,如何处理新任务 23 // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行 24 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 25 //执行初始化 26 executor.initialize(); 27 return executor; 28 } 29 30}

注意这里需要通过@EnableAsync开启异步否则无效

自定义线程任务

1public interface ILogService extends IService<Log> { 2 3 /** 4 * 分页搜索查询日志信息 5 * @param logParams 6 * @return 7 */ 8 IPage getSearchLogByPage(SearchLogParams logParams); 9 10 /** 11 * 保存日志 12 * 13 * @param logSubject 14 */ 15 @Async 16 void saveLog(LogSubject logSubject); 17}

在需要异步执行的接口,或者方法上加上@Async注解此方法就是异步方法,在主程序中调用的化,就是异步方式单独线程执行

此注解表明saveLog方法进入的线程池是KmallThreadPool方法创建的。

我们也可以单独指定方法名称@Async("saveLogs")

这样在进行日记记录的时候就是单独线程执行每次请求都快速响应了,而耗时的操作都留给线程池中的线程去异步执行

总结

Spring中用@Async注解标记的方法,称为异步方法。在spring boot应用中使用@Async很简单:

  1. 调用异步方法类上或者启动类加上注解@EnableAsync
  2. 在需要被异步调用的方法外加上@Async
  3. 所使用的@Async注解方法的类对象应该是Spring容器管理的bean对象;

注意同一个类里面调用异步方法不生效:原因默认类内的方法调用不会被aop拦截,即调用方和被调用方是在同一个类中,是无法产生切面的,该对象没有被Spring容器管理。即@Async方法不生效

解决办法:

如果要使同一个类中的方法之间调用也被拦截,需要使用spring容器中的实例对象,而不是使用默认的this,因为通过bean实例的调用才会被spring的aop拦截 本例使用方法:AsyncService asyncService = context.getBean(AsyncService.class); 然后使用这个引用调用本地的方法即可达到被拦截的目的 备注:这种方法只能拦截protected,default,public方法,private方法无法拦截。这个是spring aop的一个机制。

原理刨析

异步方法返回类型只能有两种:

  1. 当返回类型为void的时候,方法调用过程产生的异常不会抛到调用者层面,可以通过注AsyncUncaughtExceptionHandler来捕获此类异常

  2. 当返回类型为Future的时候,方法调用过程产生的异常会抛到调用者层面

注意如果不自定义异步方法的线程池默认使用SimpleAsyncTaskExecutorSimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。并发大的时候会产生严重的性能问题

Spring异步线程池接口 TaskExecutor

看源码可知

1@FunctionalInterface 2public interface TaskExecutor extends Executor { 3 void execute(Runnable var1); 4}

它的实先类有很多如下:

  1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。

  2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方

  3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类

  4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类

  5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装, ————————————————

文章参考

  1. Spring Boot(5) @Async异步线程池详解
  2. https://segmentfault.com/a/1190000040134160?utm_source=sf-similar-article
点赞
收藏

评论区

加载中...

相关推荐

spring上下文的异步Event事件

在实际开发中,我们经常会需要做一件事:在完成某一个动作之后,需要另外以同步或者异步的方式去通知另外的对象去完成额外的操作,比如:当用户下单成功之后,需要发异步消息到给到邮件系统发邮件(短信)通知用户。(这里就涉及到异步消息的概念)消息队列是我们用来解决系统与系统之间异步与解耦的极佳实践工具,而在应用内部这个级别上,有时候也会需要这样的异步消息通知机制

Flutter 异步编程指南

在App开发中,经常会遇到处理异步任务的场景,如网络请求、读写文件等。Android、iOS使用的是多线程,而在Flutter中为单线程事件循环,如下图所示

MQ应用场景

MQ常见应用场景以下介绍消息队列在实际应用中常用的使用场景。异步处理,应用解耦,流量削锋,日志处理和消息通讯四个场景。异步处理场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。(1)串行方式:将注册信息写入数据库(https://www.oschina.net/ac

01.Android线程池实践基础

目录介绍01.实际开发问题02.线程池的优势03.ThreadPoolExecutor参数04.ThreadPoolExecutor使用05.线程池执行流程06.四种线程池类07.execute和submit区别08.线程池的使用技巧01.实际开发问题

AsyncTask的用法

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程..为什么需要使用异步任务?我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避

PHP 使用 Swoole

<p在一般的Server程序中都会有一些耗时的任务,比如:发送邮件、聊天服务器发送广播等。如果我们采用同步阻塞的防水去执行这些任务,那么这肯定会非常的慢。</p<pSwoole的TaskWorker进程池可以用来执行一些异步的任务,而且不会影响接下来的任务,很适合处理以上场景。</p<p那么什么是异步任务呢?</p<p可以从下面