前言
在我们的意识里,同步执行的程序都比较符合人们的思维方式,而异步的东西通常都不好处理。在异步计算的情况下,以回调表示的动作往往会分散在代码中,也可能相互嵌套在内部,如果需要处理其中一个步骤中可能发生的错误时,情况变得更加糟糕。Java 8 引入了很多的新特性,其中就包含了 CompletableFuture 类的引入,这让我们编写清晰可读的异步代码变得更加容易,该类功能非常强大,包含了超过 50 多个方法。。。
什么是 CompletableFuture
CompletableFuture
类的设计灵感来自于 Google Guava
的 ListenableFuture 类,它实现了 Future
和 CompletionStage
接口并且新增了许多方法,它支持 lambda,通过回调利用非阻塞方法,提升了异步编程模型。它允许我们通过在与主应用程序线程不同的线程上(也就是异步)运行任务,并向主线程通知任务的进度、完成或失败,来编写非阻塞代码。
为什么要引入 CompletableFuture
Java
的 1.5 版本引入了 Future
,你可以把它简单的理解为运算结果的占位符,它提供了两个方法来获取运算结果。
get()
:调用该方法线程将会无限期等待运算结果。get(long timeout, TimeUnit unit)
:调用该方法线程将仅在指定时间timeout
内等待结果,如果等待超时就会抛出TimeoutException
异常。
Future
可以使用 Runnable
或 Callable
实例来完成提交的任务,通过其源码可以看出,它存在如下几个问题:
- 阻塞 调用
get()
方法会一直阻塞,直到等待直到计算完成,它没有提供任何方法可以在完成时通知,同时也不具有附加回调函数的功能。 - 链式调用和结果聚合处理 在很多时候我们想链接多个
Future
来完成耗时较长的计算,此时需要合并结果并将结果发送到另一个任务中,该接口很难完成这种处理。 - 异常处理
Future
没有提供任何异常处理的方式。
以上这些问题在 CompletableFuture
中都已经解决了,接下来让我们看看如何去使用 CompletableFuture
。
如何创建 CompletableFuture
最简单的创建方式就是调用 CompletableFuture.completedFuture(U value)
方法来获取一个已经完成的 CompletableFuture
对象。
@Test
public void testSimpleCompletableFuture() {
CompletableFuture<String> completableFuture = CompletableFuture.completedFuture("Hello mghio");
assertTrue(completableFuture.isDone());
try {
assertEquals("Hello mghio", completableFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
需要注意的是当我们对不完整的 CompleteableFuture
调用 get
方法的话,会由于 Future
未完成,因此 get
调用将永远阻塞,此时可以使用 CompletableFuture.complete
方法手动完成 Future
。
任务异步处理
当我们想让程序在后台异步执行任务而不关心任务的处理结果时,可以使用 runAsync
方法,该方法接收一个 Runnable
类型的参数返回 CompletableFuture<Void>
。
@Test
public void testCompletableFutureRunAsync() {
A