JDK 5 引入 Future,把「把任务交出去、过会儿再取结果」写成接口;Java 8 的 CompletableFuture 补上回调和组合,才算有完整的 Future / Promise / Callback。
Future 模式:我有任务交给 Future,期间我可以干别的,稍后再 get。接口只有五个方法。
一、Future
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
-
cancel(boolean):取消;参数决定是否立刻中断。
-
isCancelled():正常完成前被取消则 true。
-
-
get():阻塞等到结果;中断、执行异常、取消会抛相应异常。
-
get(timeout, unit):带超时,超时抛 TimeoutException。
一般和 Callable 一起用:ExecutorService.submit 返回 Future。
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(() -> { //Lambda 是一个 callable, 提交后便立即执行,这里返回的是 FutureTask 实例
System.out.println("running task");
Thread.sleep(10000);
return "return task";
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("do something else"); //前面的的 Callable 在其他线程中运行着,可以做一些其他的事情
try {
System.out.println(future.get()); //等待 future 的执行结果,执行完毕之后打印出来
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} finally {
executor.shutdown();
}
比起裸 get(),更推荐带超时的 get(long, TimeUnit),避免无限等。
二、Future 的短板与 CompletableFuture
-
-
要么
get() 阻塞变回同步,要么 isDone() 轮询耗 CPU。
Netty、Guava 都扩展过 Future。Java 8 的 CompletableFuture 吸收了 Guava ListenableFuture / SettableFuture,并把回调放到别的线程,或当作同一线程里的同步继续。任务完成后要用结果,不必干等,可 thenAccept / thenApply / thenCompose 交给下一段。
三、静态工厂:runAsync 与 supplyAsync

runAsync 返回的 CompletableFuture 没有业务返回值;supplyAsync 有。下面两段分别对应这两种工厂:
System.out.println("Hello");
});
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("CompletableFuture");
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("CompletableFuture");
四、complete 与 completeExceptionally
| 方法 |
含义 |
complete(T t) |
完成异步执行,并给出结果 |
completeExceptionally(Throwable) |
以异常结束 |
get() 会一直 block;若此时 complete,会立刻以给定值结束。只能成功一次,后续重复调用无效。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.complete("World");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
执行结果:
World
若 Future 已经能返回结果,再 complete 就无效:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete("World");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
执行结果:
Hello
completeExceptionally 则让 get 看到异常而不是成功值:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.completeExceptionally(new Exception());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
执行结果:
java.util.concurrent.ExecutionException: java.lang.Exception
...
原文系列:CompletableFuture(一)、(二)、(三)。
一句话总结:Future 只能提交再阻塞/轮询取结果;CompletableFuture 用工厂方法启动任务,再用 complete 或回调链把「完成后干什么」接上,才是 Java 8 的异步组合方式。