java中CompletableFuture的使用

之前的文章中,我们讲解了Future, 本文我们将会继续讲解java 8中引入的CompletableFuture的用法。

CompletableFuture首先是一个Future,它拥有Future所有的功能,包括获取异步执行结果,取消正在执行的任务等。

除此之外,CompletableFuture还是一个CompletionStage。

我们看下CompletableFuture的定义:

public class CompletableFuture<T> implements Future<T>, CompletionStage<T>

什么是CompletionStage呢?

在异步程序中,如果将每次的异步执行都看成是一个stage的话,我们通常很难控制异步程序的执行顺序,在javascript中,我们需要在回调中执行回调。这就会形成传说中的回调地狱。

好在在ES6中引入了promise的概念,可以将回调中的回调转写为链式调用,从而大大的提升了程序的可读性和可写性。

同样的在java中,我们使用CompletionStage来实现异步调用的链式操作。

CompletionStage定义了一系列的then*** 操作来实现这一功能。

CompletableFuture作为Future使用

调用CompletableFuture.complete方法可以立马返回结果,我们看下怎么使用这个方法来构建一个基本的Future:

    public Future<String> calculateAsync() throws InterruptedException {
CompletableFuture<String> completableFuture
= new CompletableFuture<>(); Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete("Hello");
return null;
}); return completableFuture;
}

上面我们通过调动ExecutorService来提交一个任务从而得到一个Future。如果你知道执行的结果,那么可以使用CompletableFuture的completedFuture方法来直接返回一个Future。

    public Future<String> useCompletableFuture(){
Future<String> completableFuture =
CompletableFuture.completedFuture("Hello");
return completableFuture;
}

CompletableFuture还提供了一个cancel方法来立马取消任务的执行:

    public Future<String> calculateAsyncWithCancellation() throws InterruptedException {
CompletableFuture<String> completableFuture = new CompletableFuture<>(); Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.cancel(false);
return null;
});
return completableFuture;
}

如果这个时候调用Future的get方法,将会报CancellationException异常。

Future<String> future = calculateAsyncWithCancellation();
future.get(); // CancellationException

异步执行code

CompletableFuture提供了runAsync和supplyAsync的方法,可以以异步的方式执行代码。

我们看一个runAsync的基本应用,接收一个Runnable参数:

    public  void runAsync(){
CompletableFuture<Void> runAsync= CompletableFuture.runAsync(()->{
log.info("runAsync");
});
}

而supplyAsync接受一个Supplier:

    public void supplyAsync(){
CompletableFuture<String> supplyAsync=CompletableFuture.supplyAsync(()->{
return "supplyAsync";
});
}

他们两个的区别是一个没有返回值,一个有返回值。

组合Futures

上面讲到CompletableFuture的一个重大作用就是将回调改为链式调用,从而将Futures组合起来。

而链式调用的返回值还是CompletableFuture,我们看一个thenCompose的例子:

CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));

thenCompose将前一个Future的返回结果作为后一个操作的输入。

如果我们想合并两个CompletableFuture的结果,则可以使用thenCombine:

    public void thenCombine(){
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello")
.thenCombine(CompletableFuture.supplyAsync(
() -> " World"), (s1, s2) -> s1 + s2));
}

如果你不想返回结果,则可以使用thenAcceptBoth:

    public void thenAcceptBoth(){
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenAcceptBoth(CompletableFuture.supplyAsync(() -> " World"),
(s1, s2) -> System.out.println(s1 + s2));
}

thenApply() 和 thenCompose()的区别

thenApply()和thenCompose()两个方法都可以将CompletableFuture连接起来,但是两个有点不一样。

thenApply()接收的是前一个调用返回的结果,然后对该结果进行处理。

thenCompose()接收的是前一个调用的stage,返回flat之后的的CompletableFuture。

简单点比较,两者就像是map和flatMap的区别。

并行执行任务

当我们需要并行执行任务时,通常我们需要等待所有的任务都执行完毕再去处理其他的任务,那么我们可以用到CompletableFuture.allOf方法:

    public void allOf(){
CompletableFuture<String> future1
= CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2
= CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3
= CompletableFuture.supplyAsync(() -> "World"); CompletableFuture<Void> combinedFuture
= CompletableFuture.allOf(future1, future2, future3);
}

allOf只保证task全都执行,而并没有返回值,如果希望带有返回值,我们可以使用join:

    public void join(){
CompletableFuture<String> future1
= CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2
= CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3
= CompletableFuture.supplyAsync(() -> "World"); String combined = Stream.of(future1, future2, future3)
.map(CompletableFuture::join)
.collect(Collectors.joining(" "));
}

上面的程序将会返回:“Hello Beautiful World”。

异常处理

如果在链式调用的时候抛出异常,则可以在最后使用handle来接收:

    public void handleError(){
String name = null; CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
}).handle((s, t) -> s != null ? s : "Hello, Stranger!");
}

这和Promise中的catch方法使用类似。

本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/CompletableFuture

更多教程请参考 flydean的博客

关于CompletableFuture的一切,看这篇文章就够了的更多相关文章

  1. Vue开发入门看这篇文章就够了

    摘要: 很多值得了解的细节. 原文:Vue开发看这篇文章就够了 作者:Random Fundebug经授权转载,版权归原作者所有. 介绍 Vue 中文网 Vue github Vue.js 是一套构建 ...

  2. 数据可视化之PowerQuery篇(四)二维表转一维表,看这篇文章就够了

    https://zhuanlan.zhihu.com/p/69187094 数据分析的源数据应该是规范的,而规范的其中一个标准就是数据源应该是一维表,它会让之后的数据分析工作变得简单高效. 在之前的文 ...

  3. 还不会Traefik?看这篇文章就够了!

    文章转载自:https://mp.weixin.qq.com/s/ImZG0XANFOYsk9InOjQPVA 提到Traefik,有些人可能并不熟悉,但是提到Nginx,应该都耳熟能详. 暂且我们把 ...

  4. 想让安卓app不再卡顿?看这篇文章就够了

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由likunhuang发表于云+社区专栏 实现背景 应用的使用流畅度,是衡量用户体验的重要标准之一.Android 由于机型配置和系统的 ...

  5. 微信小程序获取手机号码看这篇文章就够了

    前言 微信小程序获取手机号码,从官方文档到其他博主的文档 零零散散的 (我就是这样看过来 没有一篇满意的 也许是我搜索姿势不对) 依旧是前人栽树 后人乘凉 系列.保证看完 就可以实现获取手机号码功能 ...

  6. Kafka面试,看这篇文章就够了

    原文链接:https://mp.weixin.qq.com/s/zxPz_aFEMrshApZQ727h4g** 引言 MQ(消息队列)是跨进程通信的方式之一,可理解为异步rpc,上游系统对调用结果的 ...

  7. 讲真,MySQL索引优化看这篇文章就够了

    本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引背后的数据结构三部分相关内容,下面一一展开. 一.MySQL——索引基础 首先,我们将从索引基础开始介绍一下什么 ...

  8. spring boot入门,看这篇文章就够了

    一.SpringBoot入门 1.基本介绍 简化Spring应用开发的一个框架.整个Spring技术栈的一个大整合: J2EE开发的一站式解决方案: 优点: 快速创建独立运行的Spring项目以及与主 ...

  9. 面试官问你MySQL的优化,看这篇文章就够了

    作者:zhangqh segmentfault.com/a/1190000012155267 一.EXPLAIN 做MySQL优化,我们要善用 EXPLAIN 查看SQL执行计划. 下面来个简单的示例 ...

随机推荐

  1. 手工注入——sql server (mssql)注入实战和分析

    前言 首先要对sql server进行初步的了解.常用的全部变量@@version:返回当前的Sql server安装的版本.处理器体系结构.生成日期和操作系统.@@servername:放回运行Sq ...

  2. Android Studio使用butterknife库绑定控件ID注解

    在线导入butterknife的jar包 在Android-app-Open Module Settings下选中module下的app 选择Dependencies,点击右边的“+”,选择第一个:1 ...

  3. 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\vue.ps1,因为在此系统··········

    网上百度了下,解决方案是: 1. 以管理员身份运行PowerShell2. 执行:get-ExecutionPolicy,回复Restricted,表示状态是禁止的3.执行:set-Execution ...

  4. 掌握使用gitlab ci构建Android包的正确方式

    最近公司在做移动端的项目,自然而然的需要搭建打包的环境.本来计划用Jenkins的,但是发现在gitlab上创建完项目后,提示去配置pipeline,于是决定用gitlab去尝试下,毕竟我觉得Jenk ...

  5. 1035 Password (20分)(水)

    To prepare for PAT, the judge sometimes has to generate random passwords for the users. The problem ...

  6. JS数据结构与算法——栈

    JS数据结构与算法--栈 1.栈结构概念 栈(Stack)是一种先进后出(LIFO Last in First out)的线性表,先进栈的将会比后进栈的先出栈. 栈的限制是仅允许在一端进行插入和删除运 ...

  7. python 天天生鲜项目

    python 天天生鲜项目 django版:https://github.com/Ivy-1996/fresh flask版:https://github.com/Ivy-1996/flask-fre ...

  8. Genetic CNN: 经典NAS算法,遗传算法的标准套用 | ICCV 2017

    论文将标准的遗传算法应用到神经网络结构搜索中,首先对网络进行编码表示,然后进行遗传操作,整体方法十分简洁,搜索空间设计的十分简单,基本相当于只搜索节点间的连接方式,但是效果还是挺不错的,十分值得学习 ...

  9. CSS 布局水平 & 垂直对齐

    元素居中对齐 margin: auto; 文本居中对齐 text-align: center; 图片居中对齐 要让图片居中对齐, 可以使用 margin: auto; 并将它放到 块 元素中 左右对齐 ...

  10. String 对象-->charAt() 方法

    1.定义和用法 charAt() 方法获取指定下标的字符,下标从0开始 语法: string.charAt(index) 参数: index:指定的下标 举例:获取下标为2的字符 var str = ...