spring方法异步调用async,springboot async不起作用

  spring方法异步调用async,springboot async不起作用

  永远的神干货盘点

  背景在开发一个跳羚的项目中,需要批量的快速的插入数据库,于是想到了多线程分配次的插入数据,由于我们项目使用的跳羚项目,查看官网支持线程池

  开启异步线程@启用异步

  默认线程池threadpooltasktexecutor

  查看源码发现自动装配原理

  @ condition alon类(threadpooltaskmexecutor。class)@ Configuration(proxy bean methods=false)@ EnableConfigurationProperties(taskexecutionproperties。类)公共类TaskExecutionAutoConfiguration {/* * *应用程序的豆名称{@link TaskExecutor} .*/public静态最终字符串APPLICATION _ TASK _ EXECUTOR _ BEAN _ NAME= applicationtaskmexecutor ;@ Bean @ ConditionalOnMissingBean public TaskExecutorBuilder TaskExecutorBuilder(taskexecutorproperties属性,对象提供者TaskExecutorCustomizer taskExecutorCustomizers,对象提供者task decorator task decorator){ taskexecutorproperties .pool pool=属性。get pool();TaskExecutorBuilder builder=new TaskExecutorBuilder();建设者=建设者。队列容量(池。getqueuecapacity());建设者=建设者。corepoolsize(池。getcoresize());建设者=建设者。maxpoolsize(池。get maxsize());建设者=建设者。allowcorethreadtime out(池。isaallowcorethreadtime out());建设者=建设者。keepalive(池。getkeepalive());关闭关闭=属性。get shut down();建设者=建设者。等待终止(关闭。isawaittermination());建设者=建设者。awaitterminationperiod(关闭。getwaitterminationperiod());建设者=建设者。threadname前缀(属性。getthreadname前缀());建设者=建设者。定制器(taskexecutor定制器。有序流():迭代器);建设者=建设者。任务装饰者(task decorator。getifunique());返回构建器;} @ Lazy @ Bean(NAME={ APPLICATION _ TASK _ EXECUTOR _ Bean _ NAME,AsyncAnnotationBeanPostProcessor .DEFAULT _ TASK _ EXECUTOR _ BEAN _ NAME })@ conditionalomissingbean(EXECUTOR。class)public threadpooltasktexecutor applicationtasktexecutor(TaskExecutorBuilder builder){ return builder。build();}}TaskExecutionProperties主要做配置文件参数映射类

  @配置属性( spring。任务。执行’)公共类TaskExecutionProperties { private final Pool Pool=new Pool();对应的性能文件

  #核心线程数春天。任务。执行。游泳池。核心尺寸=8 #存活时间春天。任务。执行。游泳池。保活=60s #最大线程数春天。任务。执行。游泳池。max-size=#阻塞队列大小春天。任务。执行。游泳池。队列容量=# spring。任务。执行。线程名称前缀=名称前缀具体可以查看跳羚的参数配置《Springboot参数配置》

  自定义线程池

  从上面的默认使用的线程池的来看,缺少拒绝策略和其他的一些重要参数,为了满足我们日常的需求我们需要自定义线程吹来满足业务场景

  自定义线程池

  @配置

  @启用异步

  @Slf4j

  公共类ExecutorConfig {

  @ Value( $ { async。执行人。线程。核心池大小} )

  private int corePoolSize

  @ Value( $ { async。执行人。线程。最大池大小} )

  private int maxPoolSize

  @ Value( $ { async。执行人。线程。queue _ capacity } )

  专用int队列容量

  @ Value( $ { async。执行人。线程。姓名。前缀} )

  私有字符串名称前缀

  @ Bean(name= asyncServiceExecutor )

  公共执行程序asyncServiceExecutor() {

  日志。info( start asyncServiceExecutor-);

  threadpooltasktexecutor=new threadpooltasktexecutor();

  //配置核心线程数

  执行人。setcorepoolsize(corePoolSize);

  //配置最大线程数

  执行人。setmaxpoolsize(maxPoolSize);

  //配置队列大小

  执行人。setqueuecapacity(队列容量);

  //配置线程池中的线程的名称前缀

  执行人。setthreadname前缀(名称前缀);

  //拒绝策略:当泳池已经达到最大尺寸的时候,如何处理新任务

  //CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

  执行人。setrejectedexecutionhandler(新ThreadPoolExecutor .callerrunpolicy());

  //执行初始化

  executor.initialize().

  日志。info( end asyncServiceExecutor-);

  返回执行人;

  }

  配置文件

  # 配置核心线程数

  异步:

  执行者:

  线程:

  # 配置核心线程数

  核心池大小:5

  # 配置最大线程数

  最大池大小:5

  # 配置队列大小

  队列容量:100

  # 配置线程池中的线程的名称前缀

  名称:

  前缀:异步服务-

  测试验证

  异步服务

  @Async

  @Slf4j

  @服务

  公共类异步服务{

  * 指定线程池,当有多个线程池,需要指定名字

  * @次投掷中断异常

  @Async(asyncServiceExecutor )

  public void executeAsync(整数)引发中断的异常{

  日志。info(“start execute async”);

  线程。睡眠(2000年);

  System.out.println(异步线程执行. num);

  日志。info(“end execute async”);

  }

  控制器

  @自动连线

  AsyncServiceImpl异步服务

  @GetMapping

  公共字符串测试异步(@ request param( num )int num)引发中断的异常{

  for(int I=0;i numi ) {

  异步服务。执行异步(I);

  返回"确定";

  }

  通过邮递员测试结果如下,通过结果可以看到,我们一共请求了3次,每个任务执行的时间为2秒,但是接口会立刻返回数据好到客户端,实现了多线程异步处理的功能

  22:30:08.059[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[execute async,21] - start executeAsync

  22:30:08.059[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[execute async,21] - start executeAsync

  22:30:08.059[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[execute async,21] - start executeAsync

  异步线程执行.一

  异步线程执行.0

  异步线程执行.2

  22:30:10.072[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[execute async,24] - end executeAsync

  22:30:10.072[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[execute async,24] - end executeAsync

  22:30:10.072[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[execute async,24] - end executeAsync

  自定义可视化线程池

  虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个threadpooltasktexecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来

  @Slf4j

  公共类visiablethreadpooltasktexecutor扩展了ThreadPoolTaskExecutor {

  私有void showThreadPoolInfo(字符串前缀){

  ThreadPoolExecutor ThreadPoolExecutor=getThreadPoolExecutor();

  if (null==threadPoolExecutor) {

  返回;

  log.info({},{},taskCount [{}],completedTaskCount [{}],activeCount [{}],queueSize [{}],

  this.getThreadNamePrefix(),

  前缀,

  threadpoolexecutor。gettaskcount(),

  threadpoolexecutor。getcompletedtaskcount(),

  threadpoolexecutor。getactivecount(),

  threadPoolExecutor.getQueue().size());

  @覆盖

  公共void execute(可运行任务){

  showThreadPoolInfo(1 .do execute’);

  超级执行(任务);

  @覆盖

  public void execute(Runnable task,long startTimeout) {

  showThreadPoolInfo(2 .do execute’);

  super.execute(任务,开始超时);

  @覆盖

  大众未来?提交(可运行的任务){

  showThreadPoolInfo(1 .务必提交);

  返回超级。提交(任务);

  @覆盖

  公共测试未来测试提交(可调用测试任务){

  showThreadPoolInfo(2 .务必提交);

  返回超级。提交(任务);

  @覆盖

  公众ListenableFuture?提交列表使能(可运行的任务){

  showThreadPoolInfo(1 .do submitlistanable’);

  返回超级棒。submitlistanable(任务);

  @覆盖

  公共测试列表启用未来测试提交列表启用(可调用测试任务){

  showThreadPoolInfo(2 .do submitlistanable’);

  返回超级棒。submitlistanable(任务);

  }

  替换默认的线程池

  @ Bean(name= asyncServiceExecutor )

  公共执行程序asyncServiceExecutor() {

  日志。info( start asyncServiceExecutor-);

  //threadpooltasktexecutor=new threadpooltasktexecutor();

  //使用可视化运行状态的线程池

  threadpooltasktexecutor=new visiablethreadpooltasktexecutor();

  //配置核心线程数

  执行人。setcorepoolsize(corePoolSize);

  //配置最大线程数

  执行人。setmaxpoolsize(maxPoolSize);

  //配置队列大小

  执行人。setqueuecapacity(队列容量);

  //配置线程池中的线程的名称前缀

  执行人。setthreadname前缀(名称前缀);

  //拒绝策略:当泳池已经达到最大尺寸的时候,如何处理新任务

  //CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

  执行人。setrejectedexecutionhandler(新ThreadPoolExecutor .callerrunpolicy());

  //执行初始化

  executor.initialize().

  日志。info( end asyncServiceExecutor-);

  返回执行人;

  }

  这说明提交任务到线程池的时候,调用的是提交(可调用任务)这个方法,当前已经提交了2个任务,完成了0个,当前有2个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然;

  异步服务,2。做提交,任务计数[2],已完成任务计数[0],活动计数[2],队列大小[0]

  返回值的接口

  我们经常会使用线程池的返回值,可以使用未来测试或者CompletableFuture T接收

  异步服务

  /**

  * 指定线程池,当有多个线程池,需要指定名字

  * 带有返回值

  * @次投掷中断异常

  @Async(asyncServiceExecutor )

  public completable future String executevaluasync()引发中断的异常{

  日志。信息( start executevaluasync );

  线程。睡眠(200);

  System.out.println(异步线程执行返回结果. );

  日志。信息( end executevaluasync );

  返回completablefuture。已完成的未来(你好);

  }

  控制器

  @GetMapping(/result )

  公共字符串testVlaueAsync(@ request param( num )int num)抛出InterruptedException,ExecutionException {

  字符串结果="";

  list completable future list=new ArrayList();

  for(int I=0;i numi ) {

  可完成的未来字符串CompletableFuture=异步服务。executevaluasync();

  列表。add(completableFuture);

  for(CompletableFuture CompletableFuture:list){

  结果=结果completablefuture。get();

  返回结果;

  }

  测试结果返回正常的数据

  22:53:42.443[http-nio-8081-exec-2]INFO c . r . r . c . visiablethreadpooltasktexecutor-[showThreadPoolInfo,23] - async-service-,1 .do execute,taskCount [0],completedTaskCount [0],activeCount [0],queueSize [0]

  22:53:42.447[http-nio-8081-exec-2]INFO c . r . r . c . visiablethreadpooltasktexecutor-[showThreadPoolInfo,23] - async-service-,1 .do execute,taskCount [1],completedTaskCount [0],activeCount [1],queueSize [0]

  22:53:42.448[http-nio-8081-exec-2]INFO c . r . r . c . visiablethreadpooltasktexecutor-[showThreadPoolInfo,23] - async-service-,1 .执行,任务计数[2],完成任务计数[0],活动计数[2],队列大小[0]

  22:53:42.449[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,34]-start executevaluesync

  22:53:42.449[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,34]-start executevaluesync

  22:53:42.449[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,34]-start executevaluesync

  异步线程执行返回结果.

  异步线程执行返回结果.

  异步线程执行返回结果.

  22:53:42.662[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,37]-end executevaluesync

  22:53:42.662[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,37]-end executevaluesync

  22:53:42.662[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,37]-end executevaluesync

  22:53:42.663[http-nio-8081-exec-2]INFO c . r . r . c . test 2控制器-[testVlaueAsync,44] -结果:你好你好你好

  注意

  使用异步线程返回类型有2中空的和未来测试,其他情况都会报错

  本文主要从默认线程池,自定义线程池,可视化线程池三个角度分析了跳羚@异步实现异步线程。

  小棋子目前开放微信公众号,搜索"霸都学java "大家可以关注持续输出原创文章,欢迎关注一波

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: