首页 > 编程语言 >CompletableFuture异步编程详解

CompletableFuture异步编程详解

时间:2024-02-21 15:34:06浏览次数:37  
标签:异步 return 详解 CompletableFuture 线程 执行 public

Future介绍
先来回顾下Future,Future是JDK1.5中添加的接口,主要功能为:

获取并发的任务完成后的执行结果;
能够取消并发执行中的任务;
判断并发任务是否执行完成;
但Future也有着非常明显的缺点:

阻塞:调用 get() 方法会一直阻塞,直到等待直到计算完成;
异常处理:Future 没有提供任何异常处理的方式;
链式调用和结果聚合处理:在很多时候我们想链接多个 Future 来完成耗时较长的计算,此时需要合并结果并将结果发送到另一个任务中,该接口很难完成这种处理;
CompletableFuture类
上面介绍了Future的缺点,这些问题都可以通过CompletableFuture类解决,主要方法有:

thenApply():当执行完第一个异步程序,接着执行下一个;
thenAccept():当任务正常完成后,回调此方法;
exceptionally():当任务出现异常是,回调此方法;
anyOf():当所有的任务中,只要有一个任务完成,则主线程继续往下走;
allOf():所有的任务均完成后,则主线程继续往下走;
join():既线程合并(或者抛出一个 CompletionException 异常),阻塞当前线程,直到异步线程并发执行完成,也就是是join()方法还是异步阻塞;
supplyAsync():异步执行,有返回值;
runAsync():异步执行,无返回值;
thenCombine():两个线程完成之后进行合并返回;
创建线程
// 有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
// 无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
可以看出,supply开头的两个方法是有返回值的,而run开头的两个方法是没有返回值的,至于第二个方法传入的Executor,这个在编码中可以自定义;

thenAccept()方法
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) {
return uniApplyStage(asyncPool, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) {
return uniApplyStage(screenExecutor(executor), fn);
}
1
2
3
4
5
6
7
8
9
功能:当前任务正常完成以后,可将当前任务的返回值作为参数传给下一个任务;
执行任务A,任务A执行完成之后,将任务A返回值作为参数传入给任务B,打印结果为3:

CompletableFuture<Integer> futureA = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> futureB = futureA.thenApply((a) -> a + 2);
log.info("result:{}",futureB.get());
1
2
3
实际场景
以工作中常见的场景举个例子,例如在A服务中,调用B、C服务的结果:

公共实体:
/**
* @author 岳晓鵬
* @version 1.0
* @date 2022-06-10 00:11
*/
@Data
@Builder
public class User {

private String userName;

private Integer age;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
B、C服务代码:
/**
* B服务伪接口 获取用户姓名
* @param userId
* @return
*/
public static String getUserName(Integer userId){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "张三";
}

/**
* C服务伪接口 获取用户年龄
*
* @param userId
* @return
*/
private static Integer getAge(Integer userId){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 20;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
A服务获取数据:
public static void main(String[] args) throws ExecutionException, InterruptedException {
Long start = System.currentTimeMillis();

// 异步获取 B C服务的数据
CompletableFuture<Integer> ageFuture = CompletableFuture.supplyAsync(() -> getAge(10));
CompletableFuture<String> userNameFuture = CompletableFuture.supplyAsync(() -> getUserName(10));

// 获取用户数据
User user = User.builder()
.age(ageFuture.get())
.userName(userNameFuture.get())
.build();

Long end = System.currentTimeMillis();
log.info("执行时间:{}ms",end - start);
log.info("用户:{}",user);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
打印结果:
- 执行时间:5075ms
- 用户:User(userName=张三, age=20)
1
2
打印结果显示执行时间为 5s+,而不是 7s+,说明异步已经生效;
但其中get()方法还是阻塞的,如果线程执行时间较长,主线程将一直阻塞下去,另外还有一个get()方法可以添加超时时间:

get():阻塞获取线程执行结果;
get(long time):阻塞获取线程执行结果,如超过超时时间则继续向下执行;
getNow():阻塞获取线程执行结果,如果线程抛出异常,则返回默认值;
上面的例子也可以使用all() + join()方式获取数据:

CompletableFuture.allOf(ageFuture, userNameFuture).join();

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/xianyun1992/article/details/125245327

标签:异步,return,详解,CompletableFuture,线程,执行,public
From: https://www.cnblogs.com/xd502djj/p/18025307

相关文章

  • 多线程系列(四) -volatile关键字使用详解
    一、简介在上篇文章中,我们介绍到在多线程环境下,如果编程不当,可能会出现程序运行结果混乱的问题。出现这个原因主要是,JMM中主内存和线程工作内存的数据不一致,以及多个线程执行时无序,共同导致的结果。同时也提到引入synchronized同步锁,可以保证线程同步,让多个线程依次排队执行......
  • 实例详解在Go中构建流数据pipeline
    本文分享自华为云社区《Go并发范式流水线和优雅退出Pipeline与Cancellation》,作者:张俭。介绍Go的并发原语可以轻松构建流数据管道,从而高效利用I/O和多个CPU。本文展示了此类pipelines的示例,强调了操作失败时出现的细微之处,并介绍了干净地处理失败的技术。什么是pipeli......
  • 神经网络优化篇:详解深度学习框架(Deep Learning frameworks)
    深度学习框架一小点作者内心os:24年春节已过完,从熟悉的地方又回到陌生的地方谋生,愿新的一年都得偿所愿,心想事成。学到这会儿会发现,除非应用更复杂的模型,例如卷积神经网络,或者循环神经网络,或者当开始应用很大的模型,否则它就越来越不实用了,至少对大多数人而言,从零开始全部靠自己......
  • kafka消费组和分区关系详解
    原文链接:https://blog.csdn.net/weixin_42324471/article/details/121985212消费组概念:ConsumerGroup是Kafka提供的可扩展且有容错性的消费者机制。一个组里面有多个消费者实例,这些消费者共享一个ID,称为GroupID。组内的所有消费者协调在一起来消费订阅主题(SubscribedTopics......
  • Vue3组合式API之getCurrentInstance详解
    Vue2中,可以通过this来获取当前组件实例; Vue3中,在setup中无法通过this获取组件实例,console.log(this)打印出来的值是undefined。在Vue3中,getCurrentInstance()可以用来获取当前组件实例  vue3官方文档解释let{proxy}=getCurrentInstance(); 在setup中分别打印下面......
  • Nginx 在Linux中安装、使用、配置详解
    一、官网下载Nginx官网地址:http://nginx.org/en/download.html我下载的是最新稳定版 二、上传到服务器解压1、上传到指定的服务器地址上传的地址自己决定,我上传到/usr/Nginx。2、解压使用命令:tar-zxvf“你的Nginx压缩包”,我这里是:tar-zxvfnginx-1.24.0.ta......
  • mysql: show processlist 详解
    showprocesslist显示的信息都是来自MySQL系统库information_schema中的processlist表。所以使用下面的查询语句可以获得相同的结果:select*frominformation_schema.processlist了解这些基本信息后,下面我们看看查询出来的结果都是什么意思。Id:就是这个线程的唯一标......
  • 【转载】linux利用crontab添加定时任务详解
    crontab作用:添加,查询,删除系统计划任务的指令。[root@localhost~]#crontab[选项]选项:   -e:   编辑crontab定时任务   -l:   查询crontab任务   -r:   删除当前用户所有的crontab任务1234512345[root@localhost~]#crontab-e......
  • C语言循环队列详解
    前言相比于链队列,循环队列有着内存固定,效率高等特点,因而广泛应用于计算机的各个层面。本文主要介绍循环队列的概念和特点,列举一些循环队列的应用场景,以及给出用数组用C语言实现循环队列的代码。一、什么是循环队列?循环队列是一种特殊的线性表,特殊之处在于它只允许在表......
  • Unity基于AssetBundle资源管理流程详解
    在Unity游戏开发中,资源管理是一个非常重要的环节。随着游戏的发展,资源会变得越来越庞大,因此需要一种高效的资源管理方式来减少内存占用和加快加载速度。AssetBundle是Unity提供的一种资源打包和加载方式,可以将资源打包成一个独立的文件,然后在运行时进行加载和卸载。本文将详细介绍......