网站首页 > 精选教程 正文
一、异步调用?
1.1、同步调用
程序按定义的顺序依次执行的过程,每一行代码执行过程必须等待上一行代码执行完毕后才执行。
1.2异步调用指
程序在执行时,无需等待执行的返回值可继续执行后面的代码。显而易见,同步有依赖相关性,而异步没有,所以异步可并发执行,可提高执行效率,在相同的时间做更多的事情。
同步调用示例
public class Test {
public static Random random = new Random();
public static void doTaskFirst() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}
public static void doTaskSecond() throws Exception {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}
public static void doTaskThird() throws Exception {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10));
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}
public static void main(String[] args) throws Exception {
doTaskFirst();
doTaskSecond();
doTaskThird();
}
}
开始做任务一
完成任务一,耗时:0毫秒
开始做任务二
完成任务二,耗时:23毫秒
开始做任务三
完成任务三,耗时:5毫秒
Process finished with exit code 0
Async异步调用
在SpringBoot中使用异步调用是很简单的,只需要使用@Async注解即可实现方法的异步调用。
注意:需要在启动类或配置类加入@EnableAsync使异步调用@Async注解生效。
@EnableAsync
@SpringBootApplication
public class LzthApplication extends SpringBootServletInitializer{
使用@Async很简单,只需要在需要异步执行的方法上加入此注解即可。简单示例下
@Component
public class FileDownloadListener {
private static Logger log = LoggerFactory.getLogger(FileDownloadListener.class);
@Resource(name = "fileService")
public FileService fileService;
/**
* taskExecutor为线程池可无需配置
*/
@Async(value = "taskExecutor")
@EventListener(FileEvent.class)
public void saveDownloadRecord(FileEvent fileEvent){
log.info("文件下载记录");
Map<String, Object> source = (Map) fileEvent.getSource();
fileService.saveDownloadRecord(source);
}
}
注意事项:
- @Async既可以注解在类上也可以注解在方法上,当声明在类级别上时,调用类中的所有方法都将异步执行。就目标方法签名而言,支持任何参数类型。但是,返回类型被约束为void或java.util.concurrent.Future。
- 在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。
- 调用的异步方法,不能为同一个类的方法,简单来说,因为Spring在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的。其他的注解如@Cache等也是一样的道理,说白了,就是Spring的代理机制造成的。
异步回调及超时处理
对于一些业务场景下,需要异步回调的返回值时,就需要使用异步回调来完成了。主要就是通过Future进行异步回调。
Future类型?
Future是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果的接口。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
它的接口定义如下:
public interface Future<V> {
/**
* 用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
* 参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,
* 如果设置true,则表示可以取消正在执行过程中的任务。
* 如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,
* 即如果取消已经完成的任务会返回false;如果任务正在执行,
* 若mayInterruptIfRunning设置为true,则返回true,
* 若mayInterruptIfRunning设置为false,则返回false;
* 如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 任务是否被取消成功,如果在任务正常完成前被取消成功,则返回true
*/
boolean isCancelled();
/**
* 任务是否已经完成,若任务完成,则返回true
*/
boolean isDone();
/**
* 获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
*/
V get() throws InterruptedException, ExecutionException;
/**
* 获取执行结果,如果在指定时间内,还没获取到结果,则抛出TimeoutException,结果返回null
*/
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
Future提供了三种功能:
1、判断任务是否完成;
2、能够中断任务;
3、能够获取任务执行结果。
异步回调
新增具有Future类型返回值的异步方法和接口:
@Async
public Future<String> asyncRet(){
log.info("async task invoked -> thread: {}", Thread.currentThread().getName());
try {
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
return new AsyncResult<>("Async Result");
}
@GetMapping("/getAsyncRet")
public String doAsync() throws ExecutionException, InterruptedException {
long begin = Clock.systemUTC().millis();
log.info("start the async task: {}", begin);
Future<String> future = asyncService.asyncRet();
while (true){
if(future.isDone()){
break;
}
}
long end = Clock.systemUTC().millis();
log.info("async task time cost: {}", end - begin);
log.info("finish the async task: {}", end);
return future.get();
}
其中AsyncResult是Spring提供的一个Future接口的子类,然后通过isDone方法,判断是否已经执行完毕。此时,控制台输出:
2021-09-16 22:20:59.468 INFO 6672 --- [nio-8080-exec-1] c.b.s.controller.AsyncController : start the async task: 1547091479468
2021-09-16 22:20:59.473 INFO 6672 --- [ async-task-1-1] com.bo.springboot.service.AsyncService : async task invoked -> thread: async-task-1-1
2021-09-16 22:20:02.474 INFO 6672 --- [nio-8080-exec-1] c.b.s.controller.AsyncController : async task time cost: 3006
2021-09-16 22:20:02.474 INFO 6672 --- [nio-8080-exec-1] c.b.s.controller.AsyncController : finish the async task: 1547091482474
等待3秒后页面显示:
所以,当某个业务功能可以同时拆开一起执行时,可利用异步回调机制有效的减少程序执行时间,提高效率。
超时处理
对于一些需要异步回调的函数,不能无期限地等待下去,所以一般上需要设置超时时间,超时后可将线程释放,而不至于一直堵塞而占用资源。对于Future配置超时,很简单,通过get方法指定超时即可。
@GetMapping("/getAsyncWithTimeout")
public String getAsyncWithTimeout() throws InterruptedException, ExecutionException, TimeoutException {
Future<String> future = asyncService.asyncRet();
// 2秒后超时
return future.get(2000, TimeUnit.MILLISECONDS);
}
超时后,会抛出异常TimeoutException类,此时可进行统一异常捕获即可。
小结
该内容主要是讲解了异步调用的使用及相关配置,如超时,异常等处理。在剥离一些和业务无关的操作时,就可以考虑使用异步调用进行其他无关业务操作,以此提供业务的处理效率。或者一些业务场景下可拆分出多个方法进行同步执行又互不影响时,也可以考虑使用异步调用方式提高执行效率。
- 上一篇: Java高并发实战课程:万能通用的异步实战
- 下一篇: Dubbo异步方法调用里有个坑
猜你喜欢
- 2024-11-19 你知道@Async 是怎么让方法异步执行的吗?
- 2024-11-19 Java 并发编程 11 - 异步执行框架 Executor
- 2024-11-19 Java中异步复用io
- 2024-11-19 JAVA异步方法笔记
- 2024-11-19 面试官:Redis分布式锁超时了,任务还没执行完怎么办?
- 2024-11-19 万能通用的异步实战方案,设计多线程,mq
- 2024-11-19 Java客户端Jedis 对Redis的几种调用方式包括事务、管道、分布式
- 2024-11-19 Dubbo 2.7新特性之异步化改造
- 2024-11-19 新手也能看懂的 SpringBoot 异步编程指南
- 2024-11-19 SpringBoot 异步编程
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)