thenAccept系
参数consumer的类型是接口Consumer<T>
,这个接口里与CompletionStage相关的方法是
void accept(T t)
该方法虽然支持参数,但不支持返回值,所以thenAccept系方法返回值是CompletionStage。
thenRun系
参数是Runnable,所以action既不能接收参数也不支持返回值,所以thenRun系列方法返回的也是CompletionStage<Void>
。
Async表示异步执行fn、consumer或action。
thenCompose系
这个系列的方法会新创建出一个子流程,最终结果和thenApply系相同。
看如何使用thenApply()。
supplyAsync()启动一个异步流程,之后是两个串行操作。虽然这是一个异步流程,但任务1、2、3是串行执行,即2依赖1的执行结果,3依赖2的执行结果。
2 AND汇聚
主要是thenCombine、thenAcceptBoth和runAfterBoth系接口
3 OR汇聚
主要是applyToEither、acceptEither和runAfterEither系接口
CompletionStage applyToEither(other, fn); CompletionStage applyToEitherAsync(other, fn); CompletionStage acceptEither(other, consumer); CompletionStage acceptEitherAsync(other, consumer); CompletionStage runAfterEither(other, action); CompletionStage runAfterEitherAsync(other, action);
如何使用applyToEither()描述OR汇聚关系。
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(()->{ int t = getRandom(5, 10); sleep(t, TimeUnit.SECONDS); return String.valueOf(t); }); CompletableFuture<String> f2 = CompletableFuture.supplyAsync(()->{ int t = getRandom(5, 10); sleep(t, TimeUnit.SECONDS); return String.valueOf(t); }); CompletableFuture<String> f3 = f1.applyToEither(f2,s -> s); System.out.println(f3.join());
CompletableFuture 中各种关系(并行、串行、聚合),支持的各种场景。 比如:线程A 等待线程B或线程C等待线程A、B 。
其实CountdownLatch、ThreadPoolExecutor 和Future 就是来解决这些关系场景的,现在有了 completableFuture,可以优先考虑使用 CompletableFuture。
4 异常处理
fn、consumer、action的核心方法都不允许抛受检异常,但无法限制它们抛运行时异常,例如下面的代码,执行 1/0 就会出现除0错误的运行时异常。
非异步编程里,可以用try/catch捕获并处理异常,异步编程里该如何处理呢?
CompletionStage给出的方案很简单,使用这些方法处理异常和串行操作一样的,而且还支持链式编程。
CompletionStage exceptionally(fn); CompletionStage<R> whenComplete(consumer); CompletionStage<R> whenCompleteAsync(consumer); CompletionStage<R> handle(fn); CompletionStage<R> handleAsync(fn);
- exceptionally()类似try/catch中的catch
- whenComplete()和handle()类似try/finally的finally,无论是否发生异常都会执行whenComplete()中的回调方法consumer和handle()中的回调方法fn
whenComplete()不支持返回结果,handle()支持返回结果。
学了这么多,最后来看个例子:
//采购订单 PurchersOrder po; CompletableFuture<Boolean> cf = CompletableFuture.supplyAsync(()->{ // 在MySQL中查询规则 return findRuleByJdbc(); }).thenApply(r -> { // 规则校验 return check(po, r); }); Boolean isOk = cf.join();
如上代码问题在于:
- 读数据库属于I/O操作,应定制单独的线程池,避免线程饥饿
- 查出来的结果做为下一步处理的条件,若结果为空,没有对应处理
- 异常未处理