什么是异步调用
方法调用默认是同步的,同步就是指方法必须一行一行的往下面执行,需要等待上一行代码执行完后才能执行下面的代码,而异步调用则是不需要等待上一行代码执行完后,就可以立即执行。
使用场景
当我们的代码不需要考虑事物时,并且也不需要即时响应,这时候我们可以选择使用异步调用来减少程序的相应时间。比如注册时发送确认邮件通知、写入日志等场景
下面我们将以demo的形式来完成异步调用。
一、启动类上加注解
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
二、编写异步类
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncTask {
@Async
public String task(){
System.out.println("异步任务");
return "ok";
}
}
三、编写测试类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private AsyncTask asyncTask;
@RequestMapping("/test")
public void test(){
String content=asyncTask.task();
System.out.println(content);
System.out.println("主程序");
}
}
测试之后输出
null
主程序
异步任务
我们会发现拿到的返回值是null,所以不能够拿到异步调用的返回值,并且异步的方法也是在主程序执行完之后再去执行的。
那么应该如何监听是否执行完并拿到返回值呢,下面将解决这个问题。
修改异步类
@Async
public Future<String> task(){
System.out.println("异步任务");
return new AsyncResult<String>("执行完毕");
}
修改测试类
@RequestMapping("/test")
public void test() throws ExecutionException, InterruptedException {
Future<String> future =asyncTask.task();
while (true){
if(future.isDone()){
System.out.println( future.get());
break;
}
}
System.out.println("主程序");
}
测试之后会输出
异步任务
执行完毕
主程序
说明我们已经解决了这个问题。
这里我们用到了Future,下面我介绍下这个Future:
Future
是对于具体的Runnable
或者Callable
任务的执行结果进行取消、查询是否完成、获取结果的接口。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
他有这些方法
-
cancel(boolean mayInterruptIfRunning):用来取消任务,该方法有以下几种场景:
任务还没有被执行:参数不管为true还是false则会被取消,并且返回true。
任务已经执行完成:参数不管为true还是false则不会被取消,并且返回false。
任务已经执行但还没有执行完毕:参数为true则会取消,并且返回true,参数为false则不会取消,并且返回false
-
isCancelled():表示任务是否被取消成功。
-
isDone():表示是否执行完成
-
get():获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回。
-
get(long timeout, TimeUnit unit):获取执行结果,如果在指定时间内没有获取到结果,则返回null。
注意事项
如下方式会使@Async失效
- 异步方法使用static修饰
- 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
- 异步方法不能与调用异步方法的方法在同一个类中
- 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
- 如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
- 在Async 方法上标注@Transactional是没用的。