CompletableFuture 异步编程

本文从实例出发,介绍 CompletableFuture 基本用法。不过讲的再多,不如亲自上手练习一下。所以建议各位小伙伴看完,上机练习一把,快速掌握 CompletableFuture

全文摘要:

  • Future VS CompletableFuture

  • CompletableFuture 基本用法

0x00. 前言

一些业务场景我们需要使用多线程异步执行任务,加快任务执行速度。Java 提供 Runnable Future<V> 两个接口用来实现异步任务逻辑。

虽然 Future<V> 可以获取任务执行结果,但是获取方式十方不变。我们不得不使用Future#get 阻塞调用线程,或者使用轮询方式判断 Future#isDone 任务是否结束,再获取结果。

这两种处理方式都不是很优雅,JDK8 之前并发类库没有提供相关的异步回调实现方式。没办法,我们只好借助第三方类库,如 Guava,扩展 Future,增加支持回调功能。相关代码如下:

图片

虽然这种方式增强了 Java 异步编程能力,但是还是无法解决多个异步任务需要相互依赖的场景。

举一个生活上的例子,假如我们需要出去旅游,需要完成三个任务:

  • 任务一:订购航班

  • 任务二:订购酒店

  • 任务三:订购租车服务

很显然任务一和任务二没有相关性,可以单独执行。但是任务三必须等待任务一与任务二结束之后,才能订购租车服务。

为了使任务三时执行时能获取到任务一与任务二执行结果,我们还需要借助 CountDownLatch 。

图片

0x01. CompletableFuture

JDK8 之后,Java 新增一个功能十分强大的类:CompletableFuture。单独使用这个类就可以轻松的完成上面的需求:

图片

大家可以先不用管 CompletableFuture 相关 API,下面将会具体讲解。

对比 Future<V>CompletableFuture 优点在于:

  • 不需要手工分配线程,JDK 自动分配

  • 代码语义清晰,异步任务链式调用

  • 支持编排异步任务

怎么样,是不是功能很强大?接下来抓稳了,小黑哥要发车了。

 

1.1 方法一览

首先来通过 IDE 查看下这个类提供的方法:

图片

稍微数一下,这个类总共有 50 多个方法,我的天。。。

 

不过也不要怕,小黑哥帮你们归纳好了,跟着小黑哥的节奏,带你们掌握 CompletableFuture

若图片不清晰,可以关注『程序通事』,回复:『233』,获取该思维导图

图片

1.2 创建 CompletableFuture 实例

创建 CompletableFuture 对象实例我们可以使用如下几个方法:

图片

第一个方法创建一个具有默认结果的 CompletableFuture,这个没啥好讲。我们重点讲述下下面四个异步方法。

前两个方法 runAsync 不支持返回值,而 supplyAsync可以支持返回结果。

这个两个方法默认将会使用公共的 ForkJoinPool 线程池执行,这个线程池默认线程数是 CPU 的核数。

可以设置 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置 ForkJoinPool 线程池的线程数

使用共享线程池将会有个弊端,一旦有任务被阻塞,将会造成其他任务没机会执行。所以强烈建议使用后两个方法,根据任务类型不同,主动创建线程池,进行资源隔离,避免互相干扰。

1.3 设置任务结果

CompletableFuture 提供以下方法,可以主动设置任务结果。

1 boolean complete(T value)
2 boolean completeExceptionally(Throwable ex)

第一个方法,主动设置 CompletableFuture 任务执行结果,若返回 true,表示设置成功。如果返回 false,设置失败,这是因为任务已经执行结束,已经有了执行结果。

示例代码如下:

 1// 执行异步任务2CompletableFuture cf = CompletableFuture.supplyAsync(() -> {3  System.out.println("cf 任务执行开始");4  sleep(10, TimeUnit.SECONDS);5  System.out.println("cf 任务执行结束");6  return "楼下小黑哥";7});8//9Executors.newSingleThreadScheduledExecutor().execute(() -> {
10  sleep(5, TimeUnit.SECONDS);
11  System.out.println("主动设置 cf 任务结果");
12  // 设置任务结果,由于 cf 任务未执行结束,结果返回 true
13  cf.complete("程序通事");
14});
15// 由于 cf 未执行结束,将会被阻塞。5 秒后,另外一个线程主动设置任务结果
16System.out.println("get:" + cf.get());
17// 等待 cf 任务执行结束
18sleep(10, TimeUnit.SECONDS);
19// 由于已经设置任务结果,cf 执行结束任务结果将会被抛弃
20System.out.println("get:" + cf.get());
21/***
22   * cf 任务执行开始
23   * 主动设置 cf 任务结果
24   * get:程序通事
25   * cf 任务执行结束
26   * get:程序通事
27*/

这里需要注意一点,一旦 complete 设置成功,CompletableFuture 返回结果就不会被更改,即使后续 CompletableFuture 任务执行结束。

第二个方法,给 CompletableFuture 设置异常对象。若设置成功,如果调用 get 等方法获取结果,将会抛错。

示例代码如下:

 1// 执行异步任务2CompletableFuture cf = CompletableFuture.supplyAsync(() -> {3    System.out.println("cf 任务执行开始");4    sleep(10, TimeUnit.SECONDS);5    System.out.println("cf 任务执行结束");6    return "楼下小黑哥";7});8//9Executors.newSingleThreadScheduledExecutor().execute(() -> {
10    sleep(5, TimeUnit.SECONDS);
11    System.out.println("主动设置 cf 异常");
12    // 设置任务结果,由于 cf 任务未执行结束,结果返回 true
13    cf.completeExceptionally(new RuntimeException("啊,挂了"));
14});
15// 由于 cf 未执行结束,前 5 秒将会被阻塞。后续程序抛出异常,结束
16System.out.println("get:" + cf.get());
17/***
18 * cf 任务执行开始
19 * 主动设置 cf 异常
20 * java.util.concurrent.ExecutionException: java.lang.RuntimeException: 啊,挂了
21 * ......
22 */

1.4 CompletionStage

CompletableFuture 分别实现两个接口 Future与 CompletionStage

图片

Future 接口大家都比较熟悉,这里主要讲讲 CompletionStage

CompletableFuture 大部分方法来自CompletionStage 接口,正是因为这个接口,CompletableFuture才有如此强大功能。

想要理解 CompletionStage 接口,我们需要先了解任务的时序关系的。我们可以将任务时序关系分为以下几种:

  • 串行执行关系

  • 并行执行关系

  • AND 汇聚关系

  • OR 汇聚关系

1.5 串行执行关系

任务串行执行,下一个任务必须等待上一个任务完成才可以继续执行。

图片

CompletionStage 有四组接口可以描述串行这种关系,分别为:

图片

thenApply 方法需要传入核心参数为 Function<T,R>类型。这个类核心方法为:

1 R apply(T t)

所以这个接口将会把上一个任务返回结果当做入参,执行结束将会返回结果。

thenAccept 方法需要传入参数对象为 Consumer<T>类型,这个类核心方法为:

1void accept(T t)

返回值 void 可以看出,这个方法不支持返回结果,但是需要将上一个任务执行结果当做参数传入。

thenRun 方法需要传入参数对象为 Runnable 类型,这个类大家应该都比较熟悉,核心方法既不支持传入参数,也不会返回执行结果。

thenCompose 方法作用与 thenApply 一样,只不过 thenCompose 需要返回新的  CompletionStage。这么理解比较抽象,可以集合代码一起理解。

图片

方法中带有 Async ,代表可以异步执行,这个系列还有重载方法,可以传入自定义的线程池,上图未展示,读者只可以自行查看 API。

最后我们通过代码展示 thenApply 使用方式:

1CompletableFuture<String> cf
2        = CompletableFuture.supplyAsync(() -> "hello,楼下小黑哥")// 1
3        .thenApply(s -> s + "@程序通事") // 2
4        .thenApply(String::toUpperCase); // 3
5System.out.println(cf.join());
6// 输出结果 HELLO,楼下小黑哥@程序通事

这段代码比较简单,首先我们开启一个异步任务,接着串行执行后续两个任务。任务 2 需要等待任务1 执行完成,任务 3 需要等待任务 2。

上面方法,大家需要记住了  Function<T,R>Consumer<T>Runnable 三者区别,根据场景选择使用。

1.6 AND 汇聚关系

AND 汇聚关系代表所有任务完成之后,才能进行下一个任务。

图片

如上所示,只有任务 A 与任务 B 都完成之后,任务 C 才会开始执行。

CompletionStage 有以下接口描述这种关系。

图片

thenCombine 方法核心参数 BiFunction ,作用与 Function一样,只不过 BiFunction 可以接受两个参数,而 Function 只能接受一个参数。

thenAcceptBoth 方法核心参数BiConsumer 作用也与 Consumer一样,不过其需要接受两个参数。

runAfterBoth  方法核心参数最简单,上面已经介绍过,不再介绍。

这三组方法只能完成两个任务 AND 汇聚关系,如果需要完成多个任务汇聚关系,需要使用 CompletableFuture#allOf,不过这里需要注意,这个方法是不支持返回任务结果。

AND 汇聚关系相关示例代码,开头已经使用过了,这里再粘贴一下,方便大家理解:

图片

1.7 OR 汇聚关系

有 AND 汇聚关系,当然也存在 OR 汇聚关系。OR 汇聚关系代表只要多个任务中任一任务完成,就可以接着接着执行下一任务。

图片

CompletionStage 有以下接口描述这种关系:

图片

前面三组接口方法传参与 AND 汇聚关系一致,这里也不再详细解释了。

当然 OR 汇聚关系可以使用 CompletableFuture#anyOf 执行多个任务。

下面示例代码展示如何使用 applyToEither 完成 OR 关系。

 1CompletableFuture<String> cf2        = CompletableFuture.supplyAsync(() -> {3    sleep(5, TimeUnit.SECONDS);4    return "hello,楼下小黑哥";5});// 167CompletableFuture<String> cf2 = cf.supplyAsync(() -> {8    sleep(3, TimeUnit.SECONDS);9    return "hello,程序通事";
10});
11// 执行 OR 关系
12CompletableFuture<String> cf3 = cf2.applyToEither(cf, s -> s);
13
14// 输出结果,由于 cf2 只休眠 3 秒,优先执行完毕
15System.out.println(cf2.join());
16// 结果:hello,程序通事

1.8 异常处理

CompletableFuture  方法执行过程若产生异常,当调用 getjoin获取任务结果才会抛出异常。

图片

上面代码我们显示使用 try..catch 处理上面的异常。不过这种方式不太优雅,CompletionStage 提供几个方法,可以优雅处理异常。

图片

exceptionally 使用方式类似于 try..catch 中 catch代码块中异常处理。

whenComplete 与 handle 方法就类似于 try..catch..finanlly 中 finally 代码块。无论是否发生异常,都将会执行的。这两个方法区别在于  handle  支持返回结果。

下面示例代码展示 handle 用法:

 1CompletableFuture<Integer>2        f0 = CompletableFuture.supplyAsync(() -> (7 / 0))3        .thenApply(r -> r * 10)4        .handle((integer, throwable) -> {5            // 如果异常存在,打印异常,并且返回默认值6            if (throwable != null) {7                throwable.printStackTrace();8                return 0;9            } else {
10                // 如果
11                return integer;
12            }
13        });
14
15
16System.out.println(f0.join());
17/**
18 *java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
19 * .....
20 * 
21 * 0
22 */

0x02. 总结

JDK8 提供 CompletableFuture 功能非常强大,可以编排异步任务,完成串行执行,并行执行,AND 汇聚关系,OR 汇聚关系。

不过这个类方法实在太多,且方法还需要传入各种函数式接口,新手刚开始使用会直接会被弄懵逼。这里帮大家在总结一下三类核心参数的作用

  • Function 这类函数接口既支持接收参数,也支持返回值

  • Consumer 这类接口函数只支持接受参数,不支持返回值

  • Runnable 这类接口不支持接受参数,也不支持返回值

搞清楚函数参数作用以后,然后根据串行,AND 汇聚关系,OR 汇聚关系归纳一下相关方法,这样就比较好理解了

最后再贴一下,文章开头的思维导图,希望对你有帮助。

图片

0x03. 帮助文档

  1. 极客时间-并发编程专栏

  2. https://colobu.com/2016/02/29/Java-CompletableFuture

  3. https://www.ibm.com/developerworks/cn/java/j-cf-of-jdk8/index.html

最后说一句(求关注)

CompletableFuture 很早之前就有关注,本以为跟 Future一样,使用挺简单,谁知道学的时候才发现好难。各种 API 方法看的头有点大。

后来看到极客时间-『并发编程』专栏使用归纳方式分类 CompletableFuture 各种方法,一下子就看懂了。所以这篇文章也参考这种归纳方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/497573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

麦肯锡:物联网九大应用潜力无限 2025年经济价值高达11.1万亿美元

来源&#xff1a;WPR概要&#xff1a;物联网有可能从根本上改变人类与周围世界的互动方式。如果一切顺利的话&#xff0c;那么到2025年&#xff0c;物联网——将实体和数字世界连接起来的这项技术&#xff0c;其经济价值可达每年11&#xff0e;1万亿美元。物联网有可能从根本上…

致开发者:2018年AI技术趋势展望

来源&#xff5c;公众号“AI 前线”&#xff0c;&#xff08;ID&#xff1a;ai-front&#xff09;译者&#xff5c;核子可乐编辑&#xff5c;Emily概要&#xff1a;在 2018 年&#xff0c;开发者如何将一系列 AI 技术成果应用于当前的工作当中呢&#xff1f;近日&#xff0c;一…

CES2018:英特尔披露量子计算和神经拟态计算研究最新进展

来源&#xff1a;科学网概要&#xff1a;今年上半年&#xff0c;英特尔计划与顶级院校和研究机构共享Loihi测试芯片&#xff0c;同时将其应用于解决更加复杂的数据集和问题。1月8日&#xff0c;在拉斯维加斯举办的2018年国际消费电子产品展&#xff08;CES&#xff09;上&#…

EDA风格与Reactor模式

来源&#xff1a;https://www.cnblogs.com/ivaneye/p/10129896.html 本文将探讨如下几个问题&#xff1a; Event-Driven架构风格的约束EDA风格对架构属性的影响Reactor架构模式Reactor所解决的问题redis中的EventDriven 从观察者模式到EDA风格 GOF的23种设计模式中&#xff…

神经尘埃、脑波打字…… 2018年的脑科学要研究什么?

来源&#xff1a;科技日报概要&#xff1a;21世纪是脑科学时代。为了进一步了解大脑&#xff0c;监测大脑活动的研究进行得如火如荼&#xff0c;已在学术界和商业界掀起一股淘金热。人脑可谓人体最复杂、最神秘的器官&#xff0c;无数科学家殚精竭虑&#xff0c;也不过才揭开其…

webapp入门到实战_web前端入门到实战:前端高手在CSS 开发效率的必备片段

这篇文章会记录我们平时常用到的 CSS 片段&#xff0c;使用这些 CSS 可以帮助我们解决许多实际项目问题中遇到的&#xff0c;墙裂建议点赞收藏再看&#xff0c;方便日后查找清除浮动浮动给我们的代码带来的麻烦&#xff0c;想必不需要多说&#xff0c;我们会用很多方式来避免这…

FutureTask 示例

1、简单示例 2、泡茶 1、简单示例 // 创建 FutureTask FutureTask<Integer> futureTask new FutureTask<>(()-> 12); // 创建并启动线程 Thread t1 new Thread(futureTask); t1.start();// 获取计算结果(阻塞主线程&#xff0c;等待结果...) Integer result…

机器学习必知的8大神经网络架构和原理

来源&#xff1a;全球人工智能概要&#xff1a;有些任务直接编码较为复杂&#xff0c;我们不能处理所有的细微之处和简单编码&#xff0c;因此&#xff0c;机器学习很有必要。为什么需要机器学习&#xff1f;有些任务直接编码较为复杂&#xff0c;我们不能处理所有的细微之处和…

《科学》盘点2018可能被聚焦的研究领域

来源&#xff1a;科学网 概要&#xff1a;随着这些大趋势展现出来&#xff0c;《科学》杂志预测了今年可能被聚焦的关于研究和政策的特定领域。动荡的政治变革将在新的一年塑造科学的进程。美国总统唐纳德特朗普领导的政府预计将继续致力于废除基于科学的环境法规。英国离开欧盟…

谷歌大脑2017总结下篇:从医疗、机器人等6个领域开始的改变世界之旅

作者&#xff1a;camel概要&#xff1a;Jeff Dean发表了这篇博文的下篇&#xff0c;内容包括谷歌大脑在 AI 应用方面&#xff08;诸如医疗、机器人、创新、公平和包容等&#xff09;的工作。昨天谷歌大脑&#xff08;Google Brain&#xff09;负责人 Jeff Dean 在 Google Resea…

Gartner:人工智能将改变个人设备领域的游戏规则

来源&#xff1a;人工智能和大数据概要&#xff1a;目前&#xff0c;AI正在产生多种颠覆性力量&#xff0c;重塑我们与个人技术互动的方式。近日&#xff0c;Gartner公司预测&#xff0c;随着情感人工智能&#xff08;AI&#xff09;日臻成熟&#xff0c;个人设备到2022年将比您…

张小龙演讲干货:微信的未来在哪?这里有7个答案

来源&#xff1a;钱塘大数据概要&#xff1a;1月15日&#xff0c;以“to be正当时”为主题的2018微信公开课Pro版在广州举行&#xff0c;腾讯集团高级执行副总裁、微信事业群总裁张小龙出席并发表演讲。1月15日&#xff0c;以“to be正当时”为主题的2018微信公开课Pro版在广州…

敏捷软件开发—原则、模式与实践总结

思维导图&#xff1a;https://www.processon.com/view/link/60d46dfb5653bb049a469068

AWS VS 阿里云 VS 腾讯云 国内三大云服务商云主机评测报告

来源&#xff1a;CloudBest概要&#xff1a;近几年&#xff0c;随着国内公有云市场规模的不断增长&#xff0c;以阿里云、腾讯云为代表的本土云服务商&#xff0c;以及以AWS、微软Azure为代表的海外云服务商&#xff0c;成为公有云市场最大的赢家&#xff0c;市场寡头化趋势已经…

单一职责原则(SRP)

单一职责原则&#xff08;The Single Responsibility Principle&#xff0c;SRP&#xff09; 就一个类而言&#xff0c;应该仅有一个引起它变化的原因。 为何要把两个职责分离到单独的类中&#xff1f; 因为每一个职责都是变化的一个轴线&#xff08;an axis of change&…

多国相继出台政策法规:为「自动驾驶」的「创新发展」保驾护航

来源&#xff1a;腾讯研究院曹建峰 腾讯研究院法律研究中心高级研究员祝林华 腾讯研究院法律研究中心助理研究员人工智能等新技术&#xff0c;正从科幻概念逐步落地到各行各业&#xff0c;将从重塑驾驶、医疗、制造等领域开始&#xff0c;全面重构人类社会和生活。自动驾驶汽…

华为《5G业务商业价值评估》白皮书!

来源&#xff1a;5G蹇飒&#xff1a;华为公司商业与网络咨询部咨询专家。概要&#xff1a;在过去不长的时间里&#xff0c;5G技术不断取得重大进展&#xff0c;5G发展之路更为清晰。未来智能实验室是人工智能学家与科学院相关机构联合成立的人工智能&#xff0c;互联网和脑科学…

java正则匹配的坑_java正则表达式入坑指南

在日常开发工作中&#xff0c;无论你使用的语言是java、python、shell、golang还是C#&#xff0c; 正则表达式是编程语言中几乎绕不开的话题。有了它&#xff0c;可以帮你快速定位到符合条件的文本内容。今天小编带大家一起来学习下正则表达式&#xff0c;相信通过这篇文章的介…

这有5种来自大自然「馈赠」的AI技术及其应用,你知道多少?

原文来源&#xff1a;Towards Data Science作者&#xff1a;Luke James「雷克世界」编译&#xff1a;KABUDA对于技术领域中存在的AI相关技术&#xff0c;我们应心存感恩。人类不仅花费了数十年的时间来研究完善数学算法&#xff0c;以使这些奇妙复杂的算法发挥效用&#xff0c;…

物联网定位技术超全解析!定位正在从室外走向室内~

来源&#xff1a;物联网智库概要&#xff1a;GPS和基站定位技术基本满足了用户在室外场景中对位置服务的需求。GPS和基站定位技术基本满足了用户在室外场景中对位置服务的需求。然而&#xff0c;人的一生当中有80%的时间是在室内度过的&#xff0c;个人用户、服务机器人、新型物…