1. Overview
1.概述
ListenableFuture and CompletableFuture are built on top of Java’s Future interface. The former is part of Google’s Guava library, whereas the latter is part of Java 8.
ListenableFuture 和 CompletableFuture 构建在 Java 的 Future 接口之上。前者是 Google Guava 库的一部分,而后者是 Java 8 的一部分。
As we know, the Future interface doesn’t provide callback functionality. ListenableFuture and CompletableFuture both fill this gap. In this tutorial, we’ll learn callback mechanisms using both of them.
我们知道,Future 接口不提供回调功能。ListenableFuture 和 CompletableFuture 都填补了这一空白。在本教程中,我们将学习使用这两种机制的回调机制。
2. Callback in Async Task
2.异步任务中的回调
Let’s define a use case where we need to download image files from a remote server and persist the names of the image files in a database. Since this task consists of operations over the network and consumes time, it’s a perfect case of using Java async capability.
让我们定义一个用例:我们需要从远程服务器下载图像文件,并将图像文件名持久化到数据库中。由于这项任务包括网络操作并耗费时间,因此是使用 Java 异步功能的完美案例。
We can create a function that downloads files from a remote server and attaches a listener that then pushes data to a database when the download completes.
我们可以创建一个从远程服务器下载文件的函数,并附加一个监听器,在下载完成后将数据推送到数据库。
Let’s see how to achieve this task using both ListenableFuture and CompletableFuture.
让我们看看如何使用 ListenableFuture 和 CompletableFuture 来完成这项任务。
3. Callback in ListenableFuture
3.ListenableFuture 中的回调</em
Let’s start by adding Google’s Guava library dependency in the pom.xml:
首先,让我们在 pom.xml 中添加 Google 的 Guava 库 依赖关系:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
Now, let’s mimic a Future that downloads files from a remote web server:
现在,让我们模仿一个 Future 从远程网络服务器下载文件:
ExecutorService executorService = Executors.newFixedThreadPool(1);
ListeningExecutorService pool = MoreExecutors.listeningDecorator(executorService);
ListenableFuture<String> listenableFuture = pool.submit(downloadFile());
private static Callable<String> downloadFile() {
return () -> {
// Mimicking the downloading of a file by adding a sleep call
Thread.sleep(5000);
return "pic.jpg";
};
}
The above code creates an ExecutorService wrapped inside MoreExecutors to create a thread pool. Inside the submit method of ListenableFutureService, we pass a Callable<String> that downloads a file and returns the name of the file that returns a ListenableFuture.
上述代码创建了一个包裹在 MoreExecutors 中的 ExecutorService 以创建一个线程池。在 ListenableFutureService 的提交方法中,我们传递了一个 可调用<字符串>,该方法会下载一个文件并返回文件的名称,该文件会返回一个 ListenableFuture 。
To attach a callback on the ListenableFuture instance, Guava provides a utility method in Future:
要在 ListenableFuture 实例上附加回调,Guava 在 Future 中提供了一个实用方法:
Futures.addCallback(
listenableFuture,
new FutureCallback<>() {
@Override
public void onSuccess(String fileName) {
// code to push fileName to DB
}
@Override
public void onFailure(Throwable throwable) {
// code to take appropriate action when there is an error
}
},
executorService);
}
So, in this callback, both success and exception scenarios are handled. This way of using a callback is quite natural.
因此,在这个回调中,成功和异常情况都会得到处理。这种使用回调的方式非常自然。
We can also add a listener by adding it directly to the ListenableFuture:
我们还可以 直接将监听器添加到 ListenableFuture 中:
listenableFuture.addListener(
new Runnable() {
@Override
public void run() {
// logic to download file
}
},
executorService
);
This callback doesn’t have access to the result of Future as its input is Runnable. This callback method executes whether Future completes successfully or not.
此回调无法访问 Future 的结果,因为它的输入是 Runnable。 无论 Future 是否成功完成,此回调方法都将执行。
After going through the callbacks in ListenableFuture, the next section explores the ways in CompletableFuture to achieve the same.
在了解了 ListenableFuture 中的回调后,下一节将探讨 CompletableFuture 中实现相同目的的方法。
4. Callback in CompletableFuture
4.CompletableFuture 中的回调</em
In CompletableFuture, there are many ways to attach a callback. The most popular ways are by using chaining methods such as thenApply(), thenAccept(), thenCompose(), exceptionally(), etc., that execute normally or exceptionally.
在 CompletableFuture 中,有许多方法可以附加回调。最常用的方法是使用正常或异常执行的连锁方法,如 thenApply()、thenAccept()、thenCompose()、exceptionally() 等。
In this section, we’ll learn about a method whenComplete(). The best thing about this method is that it can be completed from any thread that wants it to be completed. Using the above file downloading example, let’s see how to use whenComplete():
在本节中,我们将学习一个方法 whenComplete()。该方法的最大优点是,它可以在任何希望完成的线程中完成。通过上面的文件下载示例,我们来看看如何使用 whenComplete() 方法:
CompletableFuture<String> completableFuture = new CompletableFuture<>();
Runnable runnable = downloadFile(completableFuture);
completableFuture.whenComplete(
(res, error) -> {
if (error != null) {
// handle the exception scenario
} else if (res != null) {
// send data to DB
}
});
new Thread(runnable).start();
private static Runnable downloadFile(CompletableFuture<String> completableFuture) {
return () -> {
try {
// logic to download to file;
} catch (InterruptedException e) {
log.error("exception is {} "+e);
}
completableFuture.complete("pic.jpg");
};
}
When downloading the file completes, the whenComplete() method executes either the success or failure conditions.
下载文件完成后,whenComplete() 方法会执行成功或失败条件。
5. Conclusion
5.结论
In this article, we learned about the callback mechanism in ListenableFuture and CompletableFuture.
在本文中,我们了解了 ListenableFuture 和 CompletableFuture 中的回调机制。
We saw that ListenableFuture is a more natural and fluid callback API when compared to CompletableFuture.
我们看到,与 CompletableFuture 相比,ListenableFuture 是一种更自然、更流畅的回调 API。
We’re free to choose what fits best to our use case as CompletableFuture is part of core Java, and ListenableFuture is part of the very popular Guava library.
CompletableFuture是核心 Java 的一部分,而ListenableFuture则是非常流行的 Guava 库的一部分,因此我们可以自由选择最适合我们使用情况的库。
All the code examples used in this article are available over on GitHub.
本文中使用的所有代码示例均可在 GitHub 上获取。