9.4 Future接口
问题
用户希望执行表示异步计算结果、检查计算是否完成、在必要时取消计算、检索计算结果等操作。
方案
使用能实现 java.util.concurrent.Future 接口的 CompletableFuture 类。
讨论
在本书介绍的 Java 8 和 Java 9 新特性中,CompletableFuture 是一种非常有用的类。由于 CompletableFuture 类可以实现 Future 接口,我们有必要对这种接口的用法做一简要回顾。
Java 5 引入的 java.util.concurrent 包让开发人员可以在更高的抽象层次上操作并发,而不仅限于使用简单的 wait 和 notify 原语。java.util.concurrent 包定义了一种名为 ExecutorService 的接口,其 submit 方法传入 Callable,并返回包装所需对象的 Future。
如例 9-15 所示,程序将任务提交给 ExecutorService 并打印字符串,然后在 Future 中检索值。
例 9-15 提交
Callable并返回Future
- ExecutorService service = Executors.newCachedThreadPool();
- Future<String> future = service.submit(new Callable<String>() {
- @Override
- public String call() throws Exception {
- Thread.sleep(100);
- return "Hello, World!";
- }
- });
- System.out.println("Processing...");
- getIfNotCancelled(future);
例 9-16 显示了 getIfNotCancelled 方法的应用。
例 9-16 在
Future中检索值
- public void getIfNotCancelled(Future<String> future) {
- try {
- if (!future.isCancelled()) { ➊
- System.out.println(future.get()); ➋
- } else {
- System.out.println("Cancelled");
- }
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
- }
❶ 检查 Future 的状态
❷ 通过阻塞调用以检索 Future 的值
在本例中,isCancelled 方法的用途不言自明;get 方法用于在 Future 中检索值,该方法属于阻塞调用(blocking call),返回的是其内部的泛型类型;getIfNotCancelled 方法采用 try/catch 代码块处理声明的异常。
输出结果如下:
Processing...Hello, World!
由于所提交的调用会立即返回 Future,程序将马上打印“Processing…”。之后调用 get 代码块直到 Future 完成,并打印结果。
既然本书重点讨论 Java 8,我们不妨采用 lambda 表达式替换 Callable 接口的匿名内部类实现,如例 9-17 所示。
例 9-17 使用 lambda 表达式并检查
Future是否完成
- future = service.submit(() -> { ➊
- Thread.sleep(10);
- return "Hello, World!";
- });
- System.out.println("More processing...");
- while (!future.isDone()) { ➋
- System.out.println("Waiting...");
- }
- getIfNotCancelled(future);
❶ 使用 lambda 表达式替换 Callable
❷ 等待 Future 完成
可以看到,除使用 lambda 表达式之外,程序还在 while 循环中调用 isDone 方法以轮询 Future,直至它完成。
在循环中使用
isDone方法称为忙等待(busy waiting),由于该方法可能产生数百万次调用,通常属于应该避免的操作。12CompletableFuture类(详见范例 9.5、范例 9.6 与范例 9.7)可以在Future完成时提供更好的处理方式。
12某些情况下可能需要忙等待,如广泛用于操作系统内核的自旋锁(spinlock)。不过,自旋锁的持有时间过长会阻止其他线程运行,导致系统性能降低。——译者注
例 9-17 的输出结果如下:
More processing...Waiting...Waiting...Waiting...// 一直等待Waiting...Waiting...Hello, World!
当某个 Future 完成时,程序显然需要一种更有效的方式来通知开发人员,计划将这个 Future 的结果用于其他操作时更是如此。CompletableFuture 类的作用就在于此。
此外,可以通过 Future 接口定义的 cancel 方法取消操作,如例 9-18 所示。
例 9-18 取消
Future
- future = service.submit(() -> {
- Thread.sleep(10);
- return "Hello, World!";
- });
- future.cancel(true);
- System.out.println("Even more processing...");
- getIfNotCancelled(future);
输出结果如下:
Even more processing...Cancelled
由于 CompletableFuture 类实现了 Future 接口,本范例讨论的所有方法同样适用于 CompletableFuture 类。
另见
有关 CompletableFuture 的讨论请参见范例 9.5、范例 9.6 与范例 9.7。
在循环中使用 