ondestroy什么时候调用_经常能够忽略的异步调用场景,结合spring boot

欢迎关注头条号:老顾聊技术

精品技术文章分享,知识的组装工


目录

  1. 什么是异步调用?
  2. 如何实现异步调用?
  3. 结合Spring boot实现异步
  4. 如何知道异步调用什么时候完成?
  5. 异步线程池
  6. 异步回调
  7. 异常处理

上一篇文章中,我们介绍了不一样的异步场景,这篇我们介绍一下常用的异步方式。

什么是异步调用?

异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行。

如何实现异步调用?

多线程,这是很多人第一眼想到的关键词,没错,多线程就是一种实现异步调用的方式。

在非spring目项目中我们要实现异步调用的就是使用多线程方式,可以自己实现Runable接口或者集成Thread类,或者使用jdk1.5以上提供了的Executors线程池。

结合SpringBoot实现异步

SpringBoot中则提供了很方便的方式执行异步调用。

  • 第一步:启动类:添加@EnableAsync注解
@SpringBootApplication@EnableAsyncpublic class Demo1Application {public static void main(String[] args) {SpringApplication.run(Demo1Application.class, args);}}
  • 第二步:新建AsyncController,在方法上添加@Async注解

@RestController

@RequestMapping("/async")

public class AsyncController {

@RequestMapping("/task")

public String doTask() throws InterruptedException{

long currentTimeMillis = System.currentTimeMillis();

this.task1();

this.task2();

this.task3();

long currentTimeMillis1 = System.currentTimeMillis();

System.out.println("task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");

return "task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";

}

@Async

public void task1() throws InterruptedException{

long currentTimeMillis = System.currentTimeMillis();

Thread.sleep(1000);

long currentTimeMillis1 = System.currentTimeMillis();

System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");

}

@Async

public void task2() throws InterruptedException{

long currentTimeMillis = System.currentTimeMillis();

Thread.sleep(2000);

long currentTimeMillis1 = System.currentTimeMillis();

System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");

}

@Async

public void task3() throws InterruptedException{

long currentTimeMillis = System.currentTimeMillis();

Thread.sleep(3000);

long currentTimeMillis1 = System.currentTimeMillis();

System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");

}

}

在doTask方法中,调用3个异步方法,观察启动结果

1b2024684e548e9d68629de82f6936c7.png

总耗时

90ffa7b8972abd3d92f1915566eb5b82.png

执行耗时日志

我们发现,并没有按照我们的需求,做异步调用,这是什么原因呢?同一个类中,方法调用是在类体内执行的,spring无法截获这个方法调用。

改造思路:将异步任务单独放到一个类中

新建异步任务类

@Componentpublic class AsyncTask { @Async  public void task1() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(1000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  }  @Async  public void task2() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(2000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  }  @Async  public void task3() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(3000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  } }

修改AsyncController

@RestController@RequestMapping("/async")public class AsyncController {@Autowired  private AsyncTask asyncTask; @RequestMapping("/task")  public String doTask() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  asyncTask.task1();  asyncTask.task2();  asyncTask.task3();  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return "task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";  } }

再启动观察效果,

1672e89ec8daa9e582238cd2aecc3c10.png

任务耗时

b4ca8fed5cfd874fa013e71831ef7cca.png

异步执行日志

我们可以看到主任务很快执行完成,只花了35ms,而其他任务在后台执行,而是各自不同的线程里面执行。这个就达到了异步调用,可以提升系统的性能。一般我们同学学到这里就结束了,没有继续钻研其他的业务场景;下面老顾介绍一下其他的场景。

如何知道异步调用什么时候完成?

如果我们要想知道三个异步任务什么时候执行完,执行的结果怎样呢?可以采用添加Fature回调方式判断

修改AsyncTask类,增加Future返回值

@Componentpublic class AsyncTask {@Async  public Future task1() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(1000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return new AsyncResult("task1返回值");}  @Async  public Future task2() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(2000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return new AsyncResult("task2返回值"); }  @Async  public Future task3() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(3000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return new AsyncResult("task3返回值"); } }

修改AsyncController

@RestController@RequestMapping("/async")public class AsyncController {@Autowired  private AsyncTask asyncTask; @RequestMapping("/task")  public String doTask() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Future taskResult1 = asyncTask.task1();  Future taskResult2 = asyncTask.task2();  Future taskResult3 = asyncTask.task3(); while(true) {  if(taskResult1.isDone() && taskResult2.isDone() && taskResult3.isDone()) {  // 三个任务都调用完成,退出循环等待  break;  }  Thread.sleep(1000);  }  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return "task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";  } }

在启动观察效果

23b64751e86e958d95a61384a03b3b1e.png
295d92bf173096050c049c49e26a24c2.png

发现总耗时变长了,那是因为主任务一直等待其他线程执行结束。我们再注意观察一下,每执行一次任务异步的线程都会重新创建,而不是从线程池中获取,这样也会导致性能下降。我们再进行改造一下。

异步线程池

定义线程池类SpringAsyncConfig

@Configuration@EnableAsyncpublic class SpringAsyncConfig { //线程池一 @Bean(name="threadPoolTaskExecutor1") public AsyncTaskExecutor taskExecutor() {  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  executor.setMaxPoolSize(10);  return executor;  }  //线程池二 @Bean(name="threadPoolTaskExecutor2") public AsyncTaskExecutor taskExecutor2() {  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  executor.setMaxPoolSize(10);  return executor;  } }

线程池已经定义好了,怎么使用,就比较简单了。

@Componentpublic class AsyncTask { @Async("threadPoolTaskExecutor1") public Future task1() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(1000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return new AsyncResult("task1返回值"); }  @Async("threadPoolTaskExecutor1")  public Future task2() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(2000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return new AsyncResult("task2返回值"); }  @Async("threadPoolTaskExecutor2")  public Future task3() throws InterruptedException{  long currentTimeMillis = System.currentTimeMillis();  Thread.sleep(3000);  long currentTimeMillis1 = System.currentTimeMillis();  System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  return new AsyncResult("task3返回值"); } }

在注解@Async注解里面指定用哪个线程池。再次启动观察,多执行几次可以看到是从线程池中获取的。

异步回调

上面我们解决了线程性能的问题,有时候我们还会遇到需要知道异步调用的返回值,根据不同的值处理不同的业务,那怎么处理呢?

我们发现Future接口中,有个get方法是返回异步调用的返回值,我们想在调用get方法时,判断异步调用是否成功,然后执行不同逻辑的业务;但get方法会把线程阻塞住,又导致了我们的http://localhost:8081/async/task请求变成的同步。我们如何能够实现异步调用成功后,我们执行成功回调方法;调用失败,我们执行失败回调方法。

这个时候我们要用到接口ListenableFuture,此接口是继承Future接口;ListenableFuture接口有addCallback这个方法增加成功回调方法,和失败回调方法。成功回调定义需要实现SuccessCallback这个接口;失败回调定义需要实现FailureCallback这个接口。

异步发送邮件案例,新建Email实体类

package com.rainbow.springboot.demo1.vo;import lombok.Data;@Datapublic class EMail { private String userName; private String emailAddr; private boolean isSuccess = false; //是否发送成功 private String message;  public EMail(String username,String emailAddr){ this.userName = username; this.emailAddr = emailAddr; }}

新建发送email任务

@Componentpublic class EMailTask { @Async public ListenableFuture send() throws InterruptedException, AsyncException{  long currentTimeMillis = System.currentTimeMillis();  EMail eMail = new EMail("rainbow
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值