业务场景
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
假如商品详情页的每个查询,需要如下标注的时间才能完成
那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然这是不能接受的。
但是如果有多个线程同时完成这 6 步操作,那么只需要 1.5s 就可以完成响应。
简介
Future
是 Java 5 添加的类,可以获取异步计算的结果。你可以使用isDone
方法检查计算是否完成,或者使用get
阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel
方法停止任务的执行。
虽然Future
以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能让计算结果完成后及时通知监听者呢(观察者设计模式)?
很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自己扩展了 Java 的 Future
接口,提供了addListener
等多个扩展方法;Google guava 也提供了通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。
作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?
最终在 jdk 1.8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture
,提供了非常强大的 Future
的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture
的方法。
CompletableFuture
类实现了 Future
接口,所以你还是可以像以前一样通过get
方法阻塞或者轮询的方式获得结果,但是不推荐使用这种方式。
创建异步对象
CompletableFuture
提供了四个静态方法来创建一个异步操作。
// 传入一个Runnable接口的实现类,无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 传入一个Runnable接口的实现类 | 自定义线程池,无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
// 传入一个要执行的异步任务,有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 传入一个要执行的异步任务 | 自定义线程池,有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync
的方法都是没有返回结果的,supplyAsync
的方法都是可以获取返回结果的- 可以传入自定义的线程池,否则就用默认的线程池;
示例
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// runAsync:无返回值,未传入自定义线程池
// CompletableFuture.runAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 5;
// System.out.println("运行结果:" + i);
// });
// supplyAsync:有返回值,传入自定义线程池
CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 5;
System.out.println("运行结果:" + i);
return i;
}, executorService);
System.out.println("supAsync的返回结果:"+supAsync.get());
}
方法完成后的感知
// 上一个异步任务执行完成之后的回调方法,里面可以获取到上一个异步任务的返回值 | 异常,使用的是同一个线程
// 虽然能得到异常信息,但是没法修改返回值
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
// 上一个异步任务完成之后,需要执行新的任务,这个任务还是以异步的方式交给线程池,让线程池自己找一个线程来处理
// 也可以获取到上一个异步任务的返回值 | 异常
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
// 上一个异步任务完成之后,指定自定义的线程池来执行新的任务
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
// 获取到上一个异步任务的异常,返回一个自定义的值
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
示例
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
// return i;
// }, executorService).whenComplete((res, thr) -> {
// // 正常情况
// System.out.println("supAsync的返回结果:" + res);
// System.out.println("异常:" + thr);
// });
CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
// 异常情况
int i = 10 / 0;
System.out.println("运行结果:" + i);
return i;
}, executorService).exceptionally(thr -> -1);
System.out.println("supAsync的返回结果:" + supAsync.get());
}
方法完成后的处理
和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值
public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
示例
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<Integer> supAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
// 模拟异常
// int i = 10 / 0;
System.out.println("运行结果:" + i);
return i;
}, executorService).handle((res, thr) -> {
if (res != null) {
return res;
}
if (thr != null) {
return -1;
}
return 0;
});
System.out.println("asyAsync的最终结果:" + supAsync.get());
}
串行化方法
// 获取到上一个线程的返回值,再执行,需要有返回值
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
// 可以获取到上一个线程的返回值,再执行
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
// 不接收上一个线程的返回值,直接执行
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
示例
public static void main(String[] args) throws Exception {
// thenRun
// CompletableFuture.supplyAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
// return i;
// }).thenRun(() -> System.out.println("任务2开始执行了"));
// thenAccept
// CompletableFuture.supplyAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
// return i;
// }).thenAccept(res -> {
// System.out.println("任务1的结果:" + res);
// });
// thenApply
CompletableFuture<Integer> apply = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}).thenApply(res -> {
System.out.println("任务1的结果:" + res);
System.out.println("任务2开始...");
return 1;
});
System.out.println("apply的最终结果:" + apply.get());
}
两任务组合-都要完成
两个任务必须都完成,触发该任务
// 传入:第2个要执行的任务 | 可以获取到任务1和任务2的返回值,需要将当前的任务值返回
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn, Executor executor)
// 传入:第2个要执行的任务 | 可以获取到任务1和任务2的返回值,无返回值
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T,? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T,? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T,? super U> action, Executor executor)
// 传入:第2个要执行的任务 | 2个任务都完成之后要做的任务,无返回值
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor)
示例
public static void main(String[] args) throws Exception {
CompletableFuture<Integer> as1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1开始");
int i = 10 / 5;
return i;
});
CompletableFuture<Integer> as2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2开始");
int i = 10 / 3;
return i;
});
// runAfterBoth
// as1.runAfterBoth(as2, () -> {
// System.out.println("任务3开始");
// });
// thenAcceptBoth
// as1.thenAcceptBoth(as2, (rs1, rs2) -> {
// System.out.println("任务1结果:" + rs1);
// System.out.println("任务2结果:" + rs2);
// });
// thenCombine
CompletableFuture<Integer> as3 = as1.thenCombine(as2, (rs1, rs2) -> rs1 + rs2);
System.out.println("任务1与任务2的结果之和:" + as3.get());
}
两任务组合-一个完成
任务1组合任务2(other),只要有一个完成,就执行任务3(action/fn)
// 任务3可以获取到任务1/任务2的返回值,任务3需要有返回值
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor)
// 任务3可以获取到任务1/任务2的返回值,任务3没有返回值
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
// 任务3无返回值
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)
示例
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(5);
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1开始...");
int i = 10 / 9;
System.out.println("任务1结束");
return i;
}, es);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2开始...");
int i = 10 / 1;
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("任务2结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
return i;
}, es);
// runAfterEitherAsync
// f1.runAfterEitherAsync(f2,() -> {
// System.out.println("任务3执行");
// },es);
// acceptEither
// f1.acceptEither(f2,res -> {
// System.out.println("结果:"+res);
// });
// applyToEither
CompletableFuture<Integer> f3 = f1.applyToEither(f2, res -> {
System.out.println("任务3开始");
int i = res * 10000;
return i;
});
System.out.println("任务3最终结果:" + f3.get());
}
多任务组合
// 所有任务都完成之后,才算结束
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
// 只要有一个任务完成,就算结束
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(5);
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("开始查询基本信息");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "华为";
}, es);
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
System.out.println("开始查询图片信息");
return "huawei.jpg";
}, es);
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> {
System.out.println("开始查询销售属性");
return "CPU品牌";
}, es);
// allOf
// CompletableFuture<Void> allOf = CompletableFuture.allOf(f1, f2, f3);
// System.out.println(f1.get() + "\t" + f2.get() + "\t" + f3.get());
// anyOf
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(f1, f2, f3);
System.out.println(anyOf.get());
}