CompletableFuture总结和实践

CompletableFuture被设计在Java中进行异步编程。异步编程意味着在主线程之外创建一个独立的线程,与主线程分隔开,并在上面运行一个非阻塞的任务,然后通知主线程进展,成功或者失败。

一、概述

1.CompletableFuture和Future的区别?

CompletableFuture和Future出现的原因是继承Thread或者实现Runnable接口的异步线程没有返回值,需要返回值的异步线程可以通过CompletableFuture和Future来创建。

CompletableFuture和Future都可以获取到异步线程的返回值,但是Future只能通过get()方法阻塞式获取,CompletableFuture由于实现了CompletionStage接口,可以通过丰富的异步回调方式来执行后续的操作,同时还能对多个任务按照先后顺序进行任务编排。

2.特性

CompletableFuture的作用主要体现在:(1)异步回调;(2)任务编排;

3.使用场景

  1. 执行比较耗时的操作时,尤其是那些依赖一个或多个远程服务的操作,使用异步任务可以改善程序的性能,加快程序的响应速度
  2. 使用CompletableFuture类,它提供了异常管理的机制,让你有机会抛出、管理异步任务执行种发生的异常
  3. 如果这些异步任务之间相互独立,或者他们之间的的某一些的结果是另一些的输入,你可以讲这些异步任务构造或合并成一个

二、原理

CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。

image-20230819001933921

Future接口主要提供get()和join()方法,可以获取任务的返回值,同时也会阻塞调用线程。

CompletionStage接口提供了丰富的执行异步调用和任务处理的接口,任务之间可以分为存在时序关系,包括串行关系、并行关系和汇聚关系等。

三、实践

1.创建任务和获取结果

(1)runAsync创建任务

通过runAsync()方法创建的异步任务没有返回值,其中有有runAsync(Runnable runnable)和runAsync(Runnable runnable, Executor executor)两个重载方法,后者比前者多一个Executor executor,即可以传入自定义的线程池,如果没传即用默认线程池(ForkJoinPool.commonPool())。

ExecutorService executor = Executors.newFixedThreadPool(3);CompletableFuture<Void> f1 = CompletableFuture.runAsync(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("f1 start");}
}, executor);

(2)supplyAsync创建任务

通过supplyAsync()方法创建的异步任务有返回值,其中有有supplyAsync(Runnable runnable)和supplyAsync(Runnable runnable, Executor executor)两个重载方法,后者比前者多一个Executor executor,即可以传入自定义的线程池,如果没传即用默认线程池(ForkJoinPool.commonPool())。

CompletableFuture<String> f2 = CompletableFuture.supplyAsync(new Supplier<String>() {@Overridepublic String get() {System.out.println("f2 start");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return "f2 return";}
}, executor);

总结:

runAsync创建的异步任务没有返回值,supplyAsync创建的异步任务有返回值。

(3)获取结果和结束任务

// 如果完成则返回结果,否则就抛出具体的异常
public T    get()
// 最大时间等待返回结果,否则就抛出具体异常
public T    get(long timeout, TimeUnit unit)
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回默认值。
public T    getNow(T valueIfAbsent)
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T    join()
//如果任务还未完成,直接给他返回值置为value
public boolean complete(T value) 

2.异步回调

(4)whenComplete和whenCompleteAsync

whenComplete()方法是当某个任务执行完成后执行的回调方法。该方法会将该任务的执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和是该任务的返回值,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常。该任务抛出的异常,一般用exceptionally()处理,其入参是异常Throwable throwable,可以有返回值。

whenComplete和whenCompleteAsync和区别在于whenComplete是在当前线程中执行该回调任务,whenCompleteAsync是会另启动一个线程来执行该回调任务,默认情况下是使用ForkJoinPool.commonPool()线程池中的线程,也可以设置自定义线程池。后面以-Async为后缀的方法也都是这样的区别。

CompletableFuture<String> f7 = f2.whenCompleteAsync((result, e) -> {System.out.println("f7 start");if (1 == 1) {throw new IllegalStateException("f7 IllegalStateException");}}
).exceptionally(new Function<Throwable, String>() {@Overridepublic String apply(Throwable throwable) {System.out.println("f8 exception: " + throwable);return "f8 return";}
});

(5)handle和handleAsync

handle方法也是当某个任务执行完成后执行的回调方法,整体功能和whenComplete方法差不多,但是handleAsync方法会有返回值,handleAsync()可以传入自定义线程池。

CompletableFuture<String> f5 = f2.handleAsync(new BiFunction<String, Throwable, String>() {@Overridepublic String apply(String s, Throwable throwable) {System.out.println("f5 start");if (1 == 1) {throw new IllegalStateException("f5 IllegalStateException");}return "f5 return";}}, executor).exceptionally(new Function<Throwable, String>() {@Overridepublic String apply(Throwable throwable) {System.out.println("f6 exception: " + throwable);return "f6 return";}
});

总结:

whenComplete和handle的区别?

whenComplete的回调方法没有返回值,handle方法有返回值?

(6)thenApply和thenApplyAsync

thenApply 表示某个任务执行完成后执行回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,带有返回值。其中

CompletableFuture<String> f3 = f2.thenApplyAsync(new Function<String, String>() {@Overridepublic String apply(String s) {System.out.println(s + ",f3 start");return "f3 return";}
}, executor);

(7)thenAccept和thenAcceptAsync

thenAccep表示某个任务执行完成后执行的回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,无返回值。

CompletableFuture<Void> f3 = f2.thenAcceptAsync(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println("f3 start");}
}, executor);

(8)thenRun和thenRunAsync

thenRun表示某个任务执行完成后执行的动作,即回调方法,无入参,无返回值。

CompletableFuture<Void> f4 = f2.thenRunAsync(new Runnable() {@Overridepublic void run() {System.out.println("f4 start");}
}, executor);

异步回调方法总结:

方法入参返回值异常处理
whenComplete有入参无返回值能抛出异常
handle有入参有返回值能抛出异常
thenApply有入参有返回值不能抛出异常
thenAccept有入参无返回值不能抛出异常
thenRun无入参无返回值不能抛出异常

3.任务编排

(9)thenCombine、thenAcceptBoth 和runAfterBoth

这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。可以理解为为:两个任务AND汇聚后才能执行。

区别:thenCombine会将两个任务的执行结果作为所提供函数的参数,且该方法有返回值;thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值;runAfterBoth没有入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。

        ExecutorService executor = Executors.newFixedThreadPool(3);CompletableFuture<String> f1 = CompletableFuture.supplyAsync(new Supplier<String>() {@Overridepublic String get() {System.out.println("f1 running");try {Thread.sleep(6000);} catch (InterruptedException e) {e.printStackTrace();}return "f1 return";}}, executor);CompletableFuture<String> f2 = CompletableFuture.supplyAsync(new Supplier<String>() {@Overridepublic String get() {System.out.println("f2 running");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return "f2 return";}}, executor);CompletableFuture<String> f30 = CompletableFuture.supplyAsync(new Supplier<String>() {@Overridepublic String get() {System.out.println("f30 running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "f30 return";}}, executor);CompletableFuture<String> f3 = f1.thenCombine(f2, new BiFunction<String, String, String>() {@Overridepublic String apply(String s, String s2) {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(s + "," + s2 + "," + "f3 running");return "f3 ruturn";}});CompletableFuture<Void> f4 = f1.thenAcceptBoth(f2, new BiConsumer<String, String>() {@Overridepublic void accept(String s, String s2) {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(s + "," + s2 + "," + "f4 running");}});

(10)applyToEither、acceptEither和runAfterEither

这三个方法和上面一样也是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。可以理解为:两个任务OR汇聚后才能执行。

区别:applyToEither会将已经完成任务的执行结果作为所提供函数的参数,且该方法有返回值;acceptEither同样将已经完成任务的执行结果作为方法入参,但是无返回值;runAfterEither没有入参,也没有返回值。

CompletableFuture<String> f6 = f1.applyToEither(f2, new Function<String, String>() {@Overridepublic String apply(String s) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(s + "," + "f6 running");return "return f6";}});CompletableFuture<Void> f7 = f1.acceptEither(f2, new Consumer<String>() {@Overridepublic void accept(String s) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(s + "," + "f7 running");}
});CompletableFuture<Void> f8 = f1.runAfterEither(f2, new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("f7 running");}
});

(11)allOf / anyOf

allOf和anyOf都是CompletableFuture的方法,他们针对的都是多任务汇聚后才能执行的逻辑,可以理解为多任务AND/OR汇聚后模式。

allOf:CompletableFuture是多个任务都执行完成后才会执行,只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。

anyOf :CompletableFuture是多个任务只要有一个任务执行完成,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回执行完成任务的结果。

CompletableFuture<Void> f9 = CompletableFuture.allOf(f1, f2, f30);
CompletableFuture<Object> f10 = CompletableFuture.anyOf(f1, f2, f30);

任务编排方法总结:

类型方法入参返回值描述
两个任务AND汇聚thenCombine有入参有返回值
两个任务AND汇聚thenAcceptBoth有入数无返回值
两个任务AND汇聚runAfterBoth无入参无返回值
两个任务OR汇聚applyToEither有入参有返回值
两个任务OR汇聚acceptEither有入参无返回值
两个任务OR汇聚runAfterEither无入参无返回值
多任务AND汇聚后模式allOf全部执行
多任务OR汇聚后模式anyOf至少一个执行

总结以上方法:

  1. 以Async结尾的方法,都是异步方法,对应的没有Async则是同步方法,一般都是一个异步方法对应一个同步方法;以Async后缀结尾的方法,都有两个重载的方法,一个是使用内容的forkjoin线程池,一种是使用自定义线程池;
  2. 以Apply开头或者结尾的方法,入口有参数,有返回值;
  3. 以supply开头的方法,入口也是没有参数的,但是有返回值;
  4. 以Accept开头或者结尾的方法,入口参数是有参数,但是没有返回值;
  5. 以run开头的方法,其入口参数一定是无参的,并且没有返回值,类似于执行Runnable方法。
  6. 和异步方法相比,任务编排方法多数带有-Both或-Either的后缀,-Both表示需要执行全部任务,-Either表示至少执行一个任务。

4.代码实现

参考资料

  1. CompletableFuture使用详解(全网看这一篇就行):https://blog.csdn.net/zsx_xiaoxin/article/details/123898171 (主要参考)

  2. CompletableFuture使用大全,简单易懂:https://juejin.cn/post/6844904195162636295

  3. CompletableFuture用法详解:https://zhuanlan.zhihu.com/p/344431341

  4. 并发编程系列-CompletableFuture:https://zhuanlan.zhihu.com/p/650700731

  5. Java CompletableFuture实现多线程异步编排:https://juejin.cn/post/7140244126679138312

  6. 使用CompletableFuture:https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650

  7. 并发编程 - CompletableFuture 解析 | 京东物流技术团队:https://zhuanlan.zhihu.com/p/646472720

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

Cesium之水流模型

关于Primitive。 Primitive和Entity&#xff0c;一般翻译成图元和实体&#xff0c;图元更接近底层&#xff0c;实体是封装后的高级对象&#xff0c;使用更加简便。一般来说&#xff0c;Primitive的使用相对繁琐&#xff0c;相比Entity需要使用者自己初始化更多对象&#xff0c…

day9 STM32 I2C总线通信

I2C总线简介 I2C总线介绍 I2C&#xff08;Inter-Integrated Circuit&#xff09;总线&#xff08;也称IIC或I2C&#xff09;是由PHILIPS公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备&#xff0c;是微电子通信控制领域广泛采用的一种总线标准。 它是同步通…

【Android】相对布局(RelativeLayout)最全解析

【Android】相对布局&#xff08;RelativeLayout&#xff09;最全解析 一、相对布局&#xff08;RelativeLayout&#xff09;概述二、根据父容器定位三、根据兄弟控件定位 一、相对布局&#xff08;RelativeLayout&#xff09;概述 相对布局&#xff08;RelativeLayout&#x…

【仿写框架之仿写Tomact】四、封装HttpRequest对象(属性映射http请求报文)、HttpResponse对象(属性映射http响应报文)

文章目录 1、创建HttpRequest对象2、创建HttpResponse对象 1、创建HttpRequest对象 HttpRequest对象中的属性与HTTP协议中的内容对应&#xff0c;用于后序servlet从request中获取请求中的参数。 参照http请求报文&#xff1a; import java.io.BufferedReader; import java…

(详解踩坑)GIT版本回滚git stash、git reset、git reset --hard、git revert

目录 背景 一、&#xff08;git log、git reflog&#xff09;查看git提交日志及命令历史 1.1 git log&#xff08;提交日志&#xff09; 1.2 git reflog&#xff08;命令历史&#xff09; 二、git reset&#xff08;回退到指定的版本&#xff0c;并且保留更改&#xff09; …

Vue-9.集成(.editorconfig、.eslintrc.js、.prettierrc)

介绍 同时使用 .editorconfig、.prettierrc 和 .eslintrc.js 是很常见的做法&#xff0c;因为它们可以在不同层面上帮助确保代码的格式一致性和质量。这种组合可以在开发过程中提供全面的代码维护和质量保证。然而&#xff0c;这也可能增加一些复杂性&#xff0c;需要谨慎配置…

Redis消息传递:发布订阅模式详解

目录 1.Redis发布订阅简介 2.发布/订阅使用 2.1 基于频道(Channel)的发布/订阅 2.2 基于模式(pattern)的发布/订阅 3.深入理解Redis的订阅发布机制 3.1 基于频道(Channel)的发布/订阅如何实现的&#xff1f; 3.2 基于模式(Pattern)的发布/订阅如何实现的&#xff1f; 3.3 Sp…

【深入探究人工智能】:常见机器学习算法总结

文章目录 1、前言1.1 机器学习算法的两步骤1.2 机器学习算法分类 2、逻辑回归算法2.1 逻辑函数2.2 逻辑回归可以用于多类分类2.3 逻辑回归中的系数 3、线性回归算法3.1 线性回归的假设3.2 确定线性回归模型的拟合优度3.3线性回归中的异常值处理 4、支持向量机&#xff08;SVM&a…

React+Typescript 状态管理

好 本文 我们来说说状态管理 也就是我们的 state 我们直接顺便写一个组件 参考代码如下 import * as React from "react";interface IProps {title: string,age: number }interface IState {count:number }export default class hello extends React.Component<I…

python 自动化学习(四) pyppeteer 浏览器操作自动化

背景 之前我在工作中涉及到了很多地方都是重复性的页面点点点工作&#xff0c;又因为安全保密原则不开放接口和数据库&#xff0c;只有一个页面来提供点击进行操作&#xff0c;就想着用前面学的自动化来实现&#xff0c;但发现前面学的模拟操作对浏览器来说并没有那么友好&…

安装pyrender和OSMesa

1&#xff09;安装 pyrender Pyrender是一个基于OpenGL的库&#xff0c;可以加载和渲染三维网格、点云、相机等对象3。 pip install pyrender 2&#xff09;理解PyOpenGL和OSMesa的关系是: PyOpenGL是Python的OpenGL绑定库&#xff08;接口壳子&#xff09;,它提供了在Python中…

代码部署到服务器

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

uniapp 企业微信侧边栏开发网页授权 注入企业权限 注入应用权限 获取userid(2)

1、网页授权&#xff0c;获取code 代码&#xff1a; oauthUrl() {const that thisuni.removeStorageSync(code)let REDIRECT_URI encodeURIComponent(window.location.href)let CORPID webConfig.appIdlet url https://open.weixin.qq.com/connect/oauth2/authorize?appi…

Flink-----Yarn应用模式作业提交流程

Yarn应用模式作业提交流程 在Yarn当中又分为Session&#xff0c;PerJob&#xff0c;Application&#xff0c;建议和推荐使用独立集群的&#xff0c;其中就包含PerJob 和Application&#xff0c;但是1.17版本的Flink已将PerJob标记为过时&#xff0c;并且Application可以解决Pe…

AI绘画之三_StableDiffusion_界面操作

1 介绍 首先&#xff0c;介绍界面中的重要元素&#xff0c;如图所示&#xff1a; 基础模型&#xff1a;基础模型是最重要的设置项文生图&#xff1a;选项卡列出了各大功能&#xff0c;文生图指通过文字生成图片图生图&#xff1a;图生图指通过图片和文字生成图片修复照片&am…

sql类型-用户定义表类型

一、创建用户定义表类型String_Table_Type CREATE TYPE String_Table_Type AS TABLE ( Id nvarchar(200) NOT NULL ) GO DECLARE test String_Table_Type INSERT INTO test VALUES(a),(b),(c) SELECT * FROM test 二、SqlSugar中使用

【IMX6ULL驱动开发学习】06.DHT11温湿度传感器驱动程序编写与测试

一、DHT11简介 DHT11是一款可测量温度和湿度的传感器。比如市面上一些空气加湿器&#xff0c;会测量空气中湿度&#xff0c;再根据测量结果决定是否继续加湿。 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器&#xff0c;具有超小体积、极低功耗的特点…

银行客户关系管理系统springboot财务金融进销存java jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 银行客户关系管理系统springboot 系统有1权限&#x…

Jetpack系列-ViewModel的使用及原理浅析

作者&#xff1a;碎星 简介 ViewModel在架构中用于承载业务逻辑和作为容器保存屏幕状态&#xff0c;它可以缓存界面的状态&#xff0c;并且能在配置变更后持久保留相应的界面状态。 在jetpack套件中&#xff0c;ViewModel随lifecycle一起提供。 优势 简介 ViewModel在架构…

第5步---MySQL的DQL查询语句

第5步---MySQL的DQL查询语句 DQL 数据库查询语言 1.基本的查询语句 1.完整得查询得语句 简化版的查询语句 select * from 表名 where 条件; 2.创建用于测试的表 1.创建测试数据 -- DQL -- 创建测试表 DROP TABLE IF EXISTS product; CREATE TABLE IF NOT EXISTS product( pi…