Java中如何使用非阻塞异步编程——CompletableFuture

分享一波:程序员赚外快-必看的巅峰干货

对于Node开发者来说,非阻塞异步编程是他们引以为傲的地方。而在JDK8中,也引入了非阻塞异步编程的概念。所谓非阻塞异步编程,就是一种不需要等待返回结果的多线程的回调方法的封装。使用非阻塞异步编程,可以很大程度上解决高并发场景的各种问题,提高程序的运行效率。
为什么要使用非阻塞异步编程

在jdk8之前,我们使用java的多线程编程,一般是通过Runnable中的run方法进行的。这种方法有个明显的缺点:没有返回值。这时候,大家会使用Callable+Future的方式去实现,代码如下。

public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future stringFuture = executor.submit(new Callable() {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return “async thread”;
}
});
Thread.sleep(1000);
System.out.println(“main thread”);
System.out.println(stringFuture.get());
}

这无疑是对高并发访问的一种缓冲方法。这种方式有一个致命的缺点就是阻塞式调用,当调用了get方法之后,会有大量的时间耗费在等待返回值之中。

不管怎么看,这种做法貌似都不太妥当,至少在代码美观性上就看起来很蛋疼。而且某些场景无法使用,比如:

多个异步线程执行时间可能不一致,我们的主线程不能一直等着。
两个异步任务之间相互独立,但是第二个依赖第一个的执行结果

在这种场景下,CompletableFuture的优势就展现出来了 。同时,CompletableFuture的封装中使用了函数式编程,这让我们的代码显得更加简洁、优雅。

不了解函数式编程的朋友,可以参考我之前的博客。JDK8新特性
CompletableFuture使用详解
runAsync和supplyAsync方法

CompletableFuture提供了四个静态方法来创建一个异步操作。

public static CompletableFuture runAsync(Runnable runnable)
public static CompletableFuture runAsync(Runnable runnable, Executor executor)
public static CompletableFuture supplyAsync(Supplier supplier)
public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。

runAsync方法不支持返回值。
supplyAsync可以支持返回值。

代码示例

/*** 无返回值** @throws Exception*/
@Test
public void testRunAsync() throws Exception {CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (Exception ignored) {}System.out.println("run end ...");});future.get();
}/*** 有返回值** @throws Exception*/
@Test
public void testSupplyAsync() throws Exception {CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {System.out.println("run end...");return System.currentTimeMillis();});Long time = future.get();System.out.println(time);
}

计算结果完成时的回调方法

当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的操作。

public CompletableFuture whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture exceptionally(Function<Throwable,? extends T> fn)

这里需要说的一点是,whenComplete和whenCompleteAsync的区别。

whenComplete:使用执行当前任务的线程继续执行whenComplete的任务。
whenCompleteAsync:使用新的线程执行任务。
exceptionally:执行出现异常时,走这个方法。

代码示例

/*** 当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。* whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。* whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。* exceptionally:执行出现异常时,走这个方法** @throws Exception*/
@Test
public void testWhenComplete() throws Exception {CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("运行结束");}).whenComplete((t, action) -> {System.out.println("执行完成");}).exceptionally(t -> {System.out.println("出现异常:" + t.getMessage());return null;});TimeUnit.SECONDS.sleep(2);
}

thenApply

当一个线程依赖另一个线程时,可以使用thenApply方法把这两个线程串行化,第二个任务依赖第一个任务的返回值。

代码示例

/*** 当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化。* 第二个任务依赖第一个任务的结果** @throws Exception*/
@Test
public void testThenApply() throws Exception {CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {long result = new Random().nextInt();System.out.println("result:" + result);return result;}).thenApply(t -> {long result = t * 5;System.out.println("result2:" + result);return result;});Long result = future.get();System.out.println(result);
}

handle

handle是执行任务完成时对结果的处理。与thenApply方法处理方式基本一致,

不同的是,handle是在任务完成后执行,不管这个任务是否出现了异常,而thenApply只可以执行正常的任务,任务出现了异常则不执行。

代码示例

/*** handle 是执行任务完成时对结果的处理。* handle 方法和 thenApply 方法处理方式基本一样。* 不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。* thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。** @throws Exception*/
@Test
public void testHandle() throws Exception {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {int i = 10 / 0;return i;}).handle((p, t) -> {int result = -1;if (t == null) {result = p * 2;} else {System.out.println(t.getMessage());}return result;});System.out.println(future.get());
}

thenAccept

thenAccept用于接收任务的处理结果,并消费处理,无返回结果。

代码示例

/*** 接收任务的处理结果,并消费处理,无返回结果。** @throws Exception*/
@Test
public void testThenAccept() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return new Random().nextInt();}).thenAccept(num -> {System.out.println(num);});System.out.println(future.get());
}

thenRun

上个任务执行完之后再执行thenRun的任务,二者只存在先后执行顺序的关系,后者并不依赖前者的计算结果,同时,没有返回值。

代码示例

/*** 该方法同 thenAccept 方法类似。不同的是上个任务处理完成后,并不会把计算的结果传给 thenRun 方法。* 只是处理玩任务后,执行 thenRun 的后续操作。** @throws Exception*/
@Test
public void testThenRun() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return new Random().nextInt();}).thenRun(() -> {System.out.println("进入了thenRun");});System.out.println(future.get());
}

thenCombine

thenCombine会把两个CompletableFuture的任务都执行完成后,把两个任务的返回值一块交给thenCombine处理(有返回值)。

代码示例

/*** thenCombine 会把 两个 CompletableFuture 的任务都执行完成后* 把两个任务的结果一块交给 thenCombine 来处理。** @throws Exception*/
@Test
public void testThenCombine() throws Exception {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "hello";}).thenCombine(CompletableFuture.supplyAsync(() -> {return "world";}), (t1, t2) -> {return t1 + " " + t2;});System.out.println(future.get());
}

thenAcceptBoth

当两个CompletableFuture都执行完成后,把结果一块交给thenAcceptBoth处理(无返回值)

代码示例

/*** 当两个 CompletableFuture 都执行完成后* 把结果一块交给thenAcceptBoth来进行消耗** @throws Exception*/
@Test
public void testThenAcceptBoth() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {return "world";}), (t1, t2) -> {System.out.println(t1 + " " + t2);});System.out.println(future.get());
}

applyToEither

两个CompletableFuture,谁执行返回的结果快,就用哪个的结果进行下一步操作(有返回值)。

代码示例

/*** 两个CompletableFuture,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作** @throws Exception*/
@Test
public void testApplyToEither() throws Exception {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return "hello";}).applyToEither(CompletableFuture.supplyAsync(() -> {return "world";}), (t) -> {return t;});System.out.println(future.get());
}

acceptEither

两个CompletableFuture,谁执行返回的结果快,就用哪个的结果进行下一步操作(无返回值)。

代码示例

/*** 两个CompletableFuture,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作。** @throws Exception*/
@Test
public void testAcceptEither() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).acceptEither(CompletableFuture.supplyAsync(() -> {return "world";}), t1 -> {System.out.println(t1);});System.out.println(future.get());
}

runAfterEither

两个CompletableFuture,任何一个完成了都会执行下一步操作

代码示例

/*** 两个CompletableFuture,任何一个完成了都会执行下一步的操作** @throws Exception*/
@Test
public void testRunAfterEither() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).runAfterEither(CompletableFuture.supplyAsync(() -> {return "world";}), () -> {System.out.println("执行完了");});System.out.println(future.get());
}

runAfterBoth

两个CompletableFuture,都完成了才会执行下一步操作。

代码示例

/*** 两个CompletableFuture,都完成了计算才会执行下一步的操作** @throws Exception*/
@Test
public void testRunAfterBoth() throws Exception {CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {return "hello";}).runAfterBoth(CompletableFuture.supplyAsync(() -> {return "world";}), () -> {System.out.println("执行完了");});System.out.println(future.get());
}

thenCompose

thenCompose方法允许对两个CompletableFuture进行流水线操作,当第一个操作完成时,将其结果作为参数传递给第二个操作。

代码示例

/*** thenCompose 方法允许你对两个 CompletableFuture 进行流水线操作,* 第一个操作完成时,将其结果作为参数传递给第二个操作。* @throws Exception*/
@Test
public void testThenCompose() throws Exception {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {int t = new Random().nextInt();System.out.println(t);return t;}).thenCompose(param -> {return CompletableFuture.supplyAsync(() -> {int t = param * 2;System.out.println("t2=" + t);return t;});});System.out.println(future.get());
}

结语

CompletableFuture是jdk8中新增的一个特性,特点是非阻塞异步编程。合理的使用非阻塞异步编程,比如将两步关联不大的操作并行处理,可以优化代码的执行效率。同时,在高并发场景下,CompletableFuture也可以进行有效的性能优化。
*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新

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

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

相关文章

城市运行一网统管_【宣传活动】持续开展城市运行“一网统管”建设宣传活动...

为进一步推进本镇城市运行“一网统管”建设工作&#xff0c;提高城市治理能力和治理水平&#xff0c;提升社会各界的知晓度和参与度&#xff0c;激发职能部门人员、党员、群众参与“一网统管”工作的热情。9月10日&#xff0c;镇网格中心于福泉居委会议室开展“推进城市运行‘一…

Java如何只使用位运算实现加减乘除

分享一波:程序员赚外快-必看的巅峰干货 前言 接前面一篇博客&#xff0c;这又是某个公司的奇葩面试题&#xff08;都说了到底是哪家公司才会出这种没营养的面试题&#xff09;。不过吐槽归吐槽&#xff0c;这个题目还是有点学问的&#xff0c;比前面那个 不使用比较运算符如何…

pmc订单表格_复工了,读一则“如何提升订单准交率和生产效率”的真实故事

故事发生在中国南方小镇上一个做办公家具的公司……家具公司创建于1995年&#xff0c;是一家集研发、生产、销售、服务为一体的现代办公家具、酒店家具制造企业。主要产品有实木班台系列、会议台系列、职员桌系列、屏风系列、沙发系列、办公座椅、酒店家具系列。在省外还有两个…

GET和POST请求到底有什么区别?

分享一波:程序员赚外快-必看的巅峰干货 看到这个标题&#xff0c;想必大部分人都已经想关掉这篇博客了。先别急&#xff0c;你真的知道这两个的区别吗&#xff1f; 做过WEB开发的朋友可能很熟悉&#xff0c;看到这个问题能立马脱口而出二者的区别。 GET在浏览器回退时是无害的…

有赞电商云应用框架设计

背景 有赞是 SaaS 公司&#xff0c;向商家提供了全方位的软件服务&#xff0c;支撑商家进行采购、店铺、商品、营销、订单、物流等等管理服务。 在这个软件服务里&#xff0c;能够满足大部分的商家&#xff0c;为商家保驾护航。 但是很多大商家往往会有自己的特殊需求&#xff…

vivado 如何创建工程模式_基于Vivado的FPGA高性能开发研修班2019年8月30日上海举行...

一、课程介绍&#xff1a;从7系列FPGA开始&#xff0c;Xilinx提出了Vivado Design Suite设计软件&#xff0c;提供全新构建的SoC 增强型、以 IP 和系统为中心的下一代开发环境&#xff0c;以解决系统级集成和实现的生产力瓶颈。同时&#xff0c;Xilinx专门针对Vivado推出了Ultr…

程序员的自我修养——远离“外包思维”

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 在我们做开发的日子里&#xff0c;不免会进行跳槽&#xff0c;跳来跳去公司无非就分成两大类——互联网公司、外包公司。当然我们本次讨论的并…

英特尔为 Kubernetes 推出分布式深度学习平台:Nauta

2019独角兽企业重金招聘Python工程师标准>>> 随着人工智能的发展&#xff0c;深度学习的价值不断增长&#xff0c;但实现它可能是一个复杂耗时的过程。英特尔(Intel)正寻求通过其在 Kubernetes 进行分布式深度学习的新开源平台来改变这一状况&#xff0c;该深度学习…

pytorch梯度下降函数_Pytorch中常用的四种优化器SGD、Momentum、RMSProp、Adam

来源&#xff1a;AINLPer微信公众号编辑: ShuYini校稿: ShuYini时间: 2019-8-16 引言很多人在使用pytorch的时候都会遇到优化器选择的问题&#xff0c;今天就给大家介绍对比一下pytorch中常用的四种优化器。SGD、Momentum、RMSProp、Adam。随机梯度下降法&#xff08;SGD&#…

python计算无穷级数求和常用公式_傅里叶变换(二) 从傅里叶级数到傅里叶变换...

在上一部分当中&#xff0c;得到了利用三角函数表示周期函数的方法&#xff0c;但是对于非周期函数就...凉了。所以有什么办法吗&#xff1f;没办法&#xff08;划掉&#xff09;。这时候我们就需要拿出来我们的黑科技——傅里叶变换。一、傅里叶级数的推广当然这东西肯定不是凭…

中鸣投篮机器人怎么组装_1000余人参加洛阳市青少年机器人竞赛

机器人智能识别地面上的黑色线条&#xff0c;并沿着线条来到指定位置&#xff0c;放下“快递包裹”&#xff1b;无人机在空中飞舞&#xff0c;时而钻过圆环&#xff0c;时而来个空翻&#xff0c;犹如跳芭蕾般在空中划过一道优美曲线&#xff1b;橘红色乒乓球从筒道中送出&#…

Exchange队列优先级介绍和配置

一、场景 在日常办公环境中所有邮件都会存在重要与非重要的情况&#xff0c;并且不同的邮箱的使用人的级别也不一样&#xff0c;不一样的职位级别要求不一样的运维等级&#xff0c;以及发送邮件要求的速度也不一样。这就导致了邮件需要按照重要性进行分类&#xff0c;重要的邮件…

Mybatis源码阅读(一):Mybatis初始化1.3 —— 解析sql片段和sql节点

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

添加请求头 retrofit_RxJava 与 Retrofit 结合的最佳实践

前言RxJava和Retrofit也火了一段时间了&#xff0c;不过最近一直在学习ReactNative和Node相关的姿势&#xff0c;一直没有时间研究这些新东西&#xff0c;最近有个项目准备写&#xff0c;打算先用Android写一个Demo出来&#xff0c;却发现Android的世界发生了天翻地覆的变化&am…

Mybatis源码阅读(二):动态节点解析2.1 —— SqlSource和SqlNode

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

k8s边缘节点_边缘计算,如何啃下集群管理这块硬骨头?

导读边缘计算平台&#xff0c;旨在将边缘端靠近数据源的计算单元纳入到中心云&#xff0c;实现集中管理&#xff0c;将云服务部署其上&#xff0c;及时响应终端请求。然而&#xff0c;成千上万的边缘节点散布于各地&#xff0c;例如银行网点、车载节点等&#xff0c;节点数量甚…

Mybatis源码阅读(二):动态节点解析2.2 —— SqlSourceBuilder与三种SqlSource

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

搞懂toString()与valueOf()的区别

一、toString&#xff08;&#xff09; 作用&#xff1a;toString&#xff08;&#xff09;方法返回一个表示改对象的字符串&#xff0c;如果是对象会返回&#xff0c;toString() 返回 “[object type]”,其中type是对象类型。 二、valueOf( ) 作用&#xff1a;valueOf房啊发返…

oracle入库的速度能到多少_倒车入库别练复杂了,其实就这两点

教练总会让学员反复练倒车入库&#xff0c;但不少学员都会有这样的疑惑&#xff1a;为什么每一次倒库结果都不一样&#xff0c;倒车入库的练习重点是什么&#xff1f;倒车入库是科二的重点及难点&#xff0c;但只要掌握以下两个关键&#xff0c;顺利通过真不难&#xff1a;01方…

Mybatis源码阅读(三):结果集映射3.1 —— ResultSetBuilder与简单映射

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…