CompletableFuture详解
平时开发过程中 Runable
、Future
、 Thread
、ExecutorService
、Callable
这些和多线程相关的class了解和使用的也比较多,相对来说更加的熟悉和了解。使用起来也更加的得心应手。但是这些组合在一起解决多线程的问题的同时也有一些不是很满足实际开发过程中的需求。然后在JDK8引入了一个新的类 CompletableFuture
来解决之前的痛点问题。接下来了解一下 CompletableFuture
的一些基本情况以及使用和注意事项。
1 CompletableFuture概述
在JDK8之前,我们使用的Java多线程变成,主要是 Thread+Runnable 来完成,但是这种方式有个弊端就是没有返回值。如果想要返回值怎么办呢,大多数人就会想到 Callable + Thread
的方式来获取到返回值。如下:
public class TestCompletable {
public static void main(String[] args) throws Exception{
FutureTask<String> task = new FutureTask((Callable<String>) () -> {
TimeUnit.SECONDS.sleep(2);
return UUID.randomUUID().toString();
});
new Thread(task).start();
String s = task.get();
System.out.println(s);
}
}
从运行上面代码可以知道当调用代码 String s = task.get();
的时候,当前主线程是阻塞状态,另一种方式获取到返回值就是通过轮询 task.isDone()
来判断任务是否做完获取返回值。因此JDK8之前提供的异步能力有一定的局限性:
- Runnable+Thread虽然提供了多线程的能力但是没有返回值。
- Callable+Thread的方法提供多线程和返回值的能力但是在获取返回值的时候会阻塞主线程。
所以上述的情况只适合不关心返回值,只要提交的Task执行了就可以。另外的就是能够容忍等待。因此我们需要更大的异步能力为了去解决这些痛点问题。比如一下场景:
- 两个Task计算合并为一个,这两个异步计算之间相互独立,但是两者之前又有依赖关系。
- 对于多个Task,只要一个任务返回了结果就返回结果
等等其他的一些负载的场景, JDK8 就引入了 CompletableFuture
1.1 CompletableFuture与Future的关系
通过上面的类继承关系图可以知道 CompletableFuture
实现了 Future
接口和 CompletionStage
。因此 CompletableFuture是对 Futrue的功能增强包含了Future的功能。从继承的另一个 CompletionStage
的名称来看完成阶段性的接口。这个怎么理解,这个就是下面要说的CompletableFuture本质。
1.2 CompletableFuture本质
CompletableFuture本质是什么?笔者的理解CompletableFuture相当于一个Task编排工具。为什么这么说依据如下:
- CompletableFuture#completedFuture、CompletableFuture#whenComplete 这些方法都是对某一个阶段Task计算完成然后进行下一步的动作。将下一个一个Task和前一个Task进行编排。
- CompletableFuture#handle 将Task串连起来
这些动作其实就是Task编排。