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的基本用法相关推荐

  1. CompletableFuture API用法介绍(二)

    文章目录 一.纯消费 API 1.thenAccep 2.thenAcceptBoth 3.runAfterBoth 4.thenRun(Runnable action) 二.组合API 1.then ...

  2. Java SE 8 在并发工具方面的加强

    本文首发于InfoQ. Java 8在Lambda表达式.接口默认方式.新的日期API等方面引入的新特性广受关注,同时在并发编程方面也做出了大量改进.以往的几个Java版本都对java.util.co ...

  3. Java异步--CompletableFuture--实例

    原文网址:Java异步--CompletableFuture--实例_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CompletableFuture这个异步编程API. JDK8新加Comp ...

  4. CompletableFuture API用法介绍(一)

    文章目录 一.前言 二.CompletableFuture 1.主动完成计算 2.创建异步任务 3.计算完成时对结果的处理 whenComplete/exceptionally/handle 4.结果 ...

  5. Java之CompletableFuture异步、组合计算基本用法

    CompletableFuture是Java8新增的Api,该类实现了Future和ComplateStage两个接口,提供了强大的Future扩展功能,可以简化异步编程的复杂性,提供了函数编程的能力 ...

  6. Java8 异步CompletableFuture 用法

    1.核心代码 CompletableFuture<Map<String, String>> onlineShopNoFuture = CompletableFuture.sup ...

  7. java8 CompletableFuture异步编程

    Future 接口的局限性 Future接口可以构建异步应用,但依然有其局限性.它很难直接表述多个Future 结果之间的依赖性.实际开发中,我们经常需要达成以下目的: 将两个异步计算合并为一个--这 ...

  8. CompletableFuture 实现异步计算

    在Markdown的语法中,<u>下划线</u>中的文字会被解析器加上下划线,为了不影响阅读,本文中JDK文档涉及到<U>都会替换为<N>,请各位注意. ...

  9. completablefuture 线程池_SpringBoot中如何优雅的使用多线程

    本文带你快速了解@Async注解的用法,包括异步方法无返回值.有返回值,最后总结了@Async注解失效的几个坑. 在 SpringBoot 应用中,经常会遇到在一个接口中,同时做事情1,事情2,事情3 ...

最新文章

  1. 世界杯决赛不好看,有点像假球,被裁判黑了?
  2. SendMessage CString
  3. redis 内存溢出_Redis为何这么快数据存储角度
  4. django前端到后端一次简单完整的请求实例
  5. file头文件_不要在头文件中使用using namespace
  6. 自己对行业未来发展的认知_我们正在建立的认知未来
  7. [持续更新] Spring Boot -Maven 指令打包相关记录
  8. 32位系统支持多大内存 Windows32位/64位系统最大支持内存详解
  9. 代码统计工具CLOC
  10. 计算机电缆能代替控制电缆吗,如何区分:计算机电缆与控制电缆!
  11. 版本控制之最佳实践(Git版)
  12. BasicRF学习心得
  13. 联想电脑 PE重装系统
  14. 【全国计算机等级考试二级教程——C语言程序设计(2021年版)编程题答案-第8章】
  15. Euclidean division
  16. JQuery - 反向选择器
  17. 腾讯搜搜高管吴军离职的传闻与真相
  18. 新库上线 | CnOpenData·IFR工业机器人数据
  19. git commit 命令出现“fatal: unable to auto-detect email address (got '******@.(none)')错误
  20. 经常聊天会日久生情吗?

热门文章

  1. Matlab中 将八进制转换为二进制,并取二进制中某一位
  2. JSP内置对象:request、response、out、session、application等内置对象
  3. 小国的游戏(素数因子和)
  4. laravel-admin使用laravel-excel导入数据
  5. 某银行系统ACS认证之TACACS+认证方案
  6. Expression 表达式动态生成
  7. 知识图谱 | 基础篇
  8. mybatis中mapper文件中的动态sql语句
  9. Arcgis pro 布局视图标注、点符号等太小问题
  10. 筋斗云效果(云朵在导航栏内随着鼠标移动)源码