CompletableFuture的基本用法
1.为什么使用CompletableFuture
JDK5新增了Future接口,用于描述一个异步计算的结果。使用Future
获得异步执行结果时,要么调用阻塞方法get()
,要么轮询看isDone()
是否为true
,这两种方法都不是很好,因为主线程也会被迫等待。
从Java 8开始引入了CompletableFuture
,它针对Future
做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
2. 基本用法
当我们想要异步处理时,以前都是调用execute或subject方法,而CompletableFuture有自己的一些方法可以异步执行线程
2.1 最基本的异步执行
2.1.1 runAysnc
此方法可以异步处理某个任务,传入的对象应该是实现了Runnable接口。也就是说,这个任务没有输入参数,也不能有输出参数
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> System.out.println("hello world"));
2.1.2 supplyAsync
此方法也可以异步处理任务,传入的对象应该实现了Supplier接口。Supplier英文为供应商,即可以进行生产,返回数据。将Supplier作为参数并返回CompletableFuture<U>
结果值,这意味着它不接受任何输入参数,而是将result作为输出返回。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello world");
System.out.println(future.get());-----结果为hello world
2.2 任务的顺序执行
当多个任务有依赖关系,即有先后执行顺序时,可以调用thenApply,thenRun(),thenAccept()等方法
2.2.1 thenRun
此方法不接受参数,也不返回结果,不进不出
CompletableFuture.runAsync(() -> System.out.println("hello world")).thenRun(() -> System.out.println("test"));
2.2.2 thenAccept
此方法接受参数,但不返回结果,只进不出
CompletableFuture.supplyAsync(() -> "hello world").thenAccept( i -> System.out.println( i + " test"));
2.2.3 thenApply
此方法可以将上一次任务执行完成后的返回结果 作为此次任务的输入参数执行,并可以将此次任务的执行结果返回,即是说该方法即可输入参数,也可输出结果
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello world").thenApply( i -> i + " test");
System.out.println(future.get());-----结果为hello world test
2.3 任务的合并
假设有这么一种业务场景,需要将多个非常耗时任务的结果进行额外处理,比如将他们返回的字符串全部拼接起来,那这种业务场景使用2.3中的thenApply也可以实现,但是使用thenApply就需要依次执行这些任务,不能并发执行,这样会导致很耗时
2.3.1 thenCombine
那这时我们就可以使用thenCombine方法,相比于thenApply,thenCombine的优势在于多个任务可以并行处理,最后合并结果返回新的CompletableFuture<U>对象
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "zhang");
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> " san");
future.thenCombine(future1, (f1, f2) -> f1 + f2).whenComplete((r,e) -> {System.out.println(r);
});
---结果----
zhang san
2.3.2 thenAcceptBoth
该方法与thenCombine基本一样,两个future也是并行处理的,和thenCombine唯一不同的是,它是纯消费的,不返回数据,即返回的对象是CompletableFuture<Void>
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "zhang");
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> " san");
future.thenAcceptBoth(future1, (x, y) -> System.out.println(x + y));
-----结果-----
zhang san
上面讲的是两个任务的合并,那还有一种情况是,只要两个任务中的任意一个执行完成,我们就当作合并完成,舍弃未完成的任务,从而进行返回CompletableFuture对象
2.3.3 acceptEither(无返回值)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "zhang");
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> " san");
future.acceptEither(future1, x -> System.out.println(x ));
---- 结果----
zhang
2.3.3 applyToEither(有返回值)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "zhang");
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> " san");
future.applyToEither(future1, x -> x).whenComplete((r,e) -> {System.out.println(r);
});
-----结果-----
zhang
2.4 任务完成后的处理
当所有任务执行完后,你想进行某一些特定操作,就可以使用whenComplete方法
2.4.1 whenComplete
此方法接受2个参数,第一个参数为上一次任务的返回结果,第二个参数表示异常对象,该方法是只消费的,不会有返回结果
CompletableFuture.supplyAsync(() -> "hello world").whenComplete((r, e) -> {System.out.println(r);System.out.println(e);
});
-----结果----
hello world
null
CompletableFuture.supplyAsync(() -> 1/0).whenComplete((r, e) -> {System.out.println(r);System.out.println(e);
});
----结果----
null
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
2.4.2 handle
和whenComplete方法类似的是此方法也是接受2个参数,第一个参数为上一次任务的返回结果,第二个参数表示异常对象,
但是不同的是handle需要有返回结果,所以这个方法的使用场景可以是:尽管之前的执行的任务异常,仍然需要有默认返回值
CompletableFuture<Integer> handle = CompletableFuture.supplyAsync(() -> 4 / 0).handle((i, e) -> Objects.nonNull(e) ? 0 : i +1);
System.out.println(handle.get());
----结果----
0
2.4.3 exceptionally
此方法和handle类似,不过它只接受一个参数即异常对象,且也是需要返回一个结果
CompletableFuture.supplyAsync(() -> 1/0).exceptionally(e -> 0).whenComplete((r,e) -> {System.out.println(r);
});
2.5 线程堵塞
当我们想在任务执行完成后,再执行接下来的主线程,那么就需要将线程堵塞。
2.5.1 get()
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {Thread.sleep(2000L);return "haha";
});
String s = future.get();
System.out.println(s);
System.out.println("test");
----结果----
2秒后同时打印haha和test
2.5.2 join()
和get用法一样,都可以用来堵塞主线程,且可以获取到future的值,不同的是,当他们抛出异常时会有所区别。
2.6 foreach 和 CompletableFuture 结合
在进行业务处理时,我们经常需要对一个List结合中的所有对象进行异步处理,那么一般的写法就是
List<Integer> num = Lists.newArrayList(1,2,3,4);
num.forEach(n -> threadPool.execute(() -> System.out.println(n)));
但是这种方法不灵活,比如,我现在想在执行完1,2,3,4这四个任务后,打印finish。使用java5的Future是做不了的,除非使用get方法堵塞线程,那显然这很不好。
可以使用下面的方法做,为了更容易看懂,就不使用strem的写法了
List<Integer> num = Lists.newArrayList(1,2,3,4);
List<CompletableFuture<Void>> futures = Lists.newArrayList();
num.forEach(n -> {CompletableFuture<Void> future = CompletableFuture.runAsync(() -> System.out.println(n));futures.add(future);
});
CompletableFuture.allOf(futures.toArray(new
CompletableFuture[futures.size()])).whenComplete((r, e) -> {System.out.println("finish");
});
System.out.println("test");
------结果----
1
2
test
4
3
finish
从结果可以看出来,线程并没有堵塞,且在4个任务执行完后打印了finish
CompletableFuture.allOf(CompletableFuture... futures):此方法传入一个CompletableFuture的可变数组,当该数组中的所有CompletableFuture任务都执行完毕后才会返回一个新的CompletableFuture任务供后续使用
与之类似的还有CompletableFuture.anyOf(CompletableFuture... futures):此方法是当数组中任意一个CompletableFuture
执行完后就会返回一个新的CompletableFuture任务对象
CompletableFuture的基本用法相关推荐
- CompletableFuture API用法介绍(二)
文章目录 一.纯消费 API 1.thenAccep 2.thenAcceptBoth 3.runAfterBoth 4.thenRun(Runnable action) 二.组合API 1.then ...
- Java SE 8 在并发工具方面的加强
本文首发于InfoQ. Java 8在Lambda表达式.接口默认方式.新的日期API等方面引入的新特性广受关注,同时在并发编程方面也做出了大量改进.以往的几个Java版本都对java.util.co ...
- Java异步--CompletableFuture--实例
原文网址:Java异步--CompletableFuture--实例_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CompletableFuture这个异步编程API. JDK8新加Comp ...
- CompletableFuture API用法介绍(一)
文章目录 一.前言 二.CompletableFuture 1.主动完成计算 2.创建异步任务 3.计算完成时对结果的处理 whenComplete/exceptionally/handle 4.结果 ...
- Java之CompletableFuture异步、组合计算基本用法
CompletableFuture是Java8新增的Api,该类实现了Future和ComplateStage两个接口,提供了强大的Future扩展功能,可以简化异步编程的复杂性,提供了函数编程的能力 ...
- Java8 异步CompletableFuture 用法
1.核心代码 CompletableFuture<Map<String, String>> onlineShopNoFuture = CompletableFuture.sup ...
- java8 CompletableFuture异步编程
Future 接口的局限性 Future接口可以构建异步应用,但依然有其局限性.它很难直接表述多个Future 结果之间的依赖性.实际开发中,我们经常需要达成以下目的: 将两个异步计算合并为一个--这 ...
- CompletableFuture 实现异步计算
在Markdown的语法中,<u>下划线</u>中的文字会被解析器加上下划线,为了不影响阅读,本文中JDK文档涉及到<U>都会替换为<N>,请各位注意. ...
- completablefuture 线程池_SpringBoot中如何优雅的使用多线程
本文带你快速了解@Async注解的用法,包括异步方法无返回值.有返回值,最后总结了@Async注解失效的几个坑. 在 SpringBoot 应用中,经常会遇到在一个接口中,同时做事情1,事情2,事情3 ...
最新文章
- 世界杯决赛不好看,有点像假球,被裁判黑了?
- SendMessage CString
- redis 内存溢出_Redis为何这么快数据存储角度
- django前端到后端一次简单完整的请求实例
- file头文件_不要在头文件中使用using namespace
- 自己对行业未来发展的认知_我们正在建立的认知未来
- [持续更新] Spring Boot -Maven 指令打包相关记录
- 32位系统支持多大内存 Windows32位/64位系统最大支持内存详解
- 代码统计工具CLOC
- 计算机电缆能代替控制电缆吗,如何区分:计算机电缆与控制电缆!
- 版本控制之最佳实践(Git版)
- BasicRF学习心得
- 联想电脑 PE重装系统
- 【全国计算机等级考试二级教程——C语言程序设计(2021年版)编程题答案-第8章】
- Euclidean division
- JQuery - 反向选择器
- 腾讯搜搜高管吴军离职的传闻与真相
- 新库上线 | CnOpenData·IFR工业机器人数据
- git commit 命令出现“fatal: unable to auto-detect email address (got '******@.(none)')错误
- 经常聊天会日久生情吗?