CompletableFuture:利用JUC包进行异步编程

多线程的优化

串行和并行

实际上多线程就是将各个串行操作,变成并行操作,最大化利用等待的时间。因此多线程实现时,我们主要要实现的业务逻辑就是分工和协作
多线程实现也一般伴随异步化。那么什么是同步和异步呢?异步中,单线程和多线程又有什么区别呢

同步和异步

假设有两个任务A 和 B
同步:执行A时,只能等待A结束,取得结果,再执行B,即使执行A时资源有空闲,也只能先执行A。
异步:

  1. 单线程: 先执行A, 过一段时间 t1,不管A是否执行完,也得停下A任务,开始处理B了。
  2. 多线程:两个线程,一个执行A, 一个执行B

CompletableFuture 的核心优势

  1. 无需手工维护线程,没有繁琐的手工维护线程的工作,给任务分配线程的工作也不需要我们关注;
  2. 语义更清晰,例如 f3 = f1.thenCombine(f2, ()->{}) 能够清晰地表述“任务 3 要等待任务 1 和任务 2 都完成后才能开始”;
  3. 代码更简练并且专注于业务逻辑,几乎所有代码都是业务逻辑相关的。

创建 CompletableFuture 对象

// 使用默认线程池
static CompletableFuture<Void>runAsync(Runnable runnable)
static <U> CompletableFuture<U>supplyAsync(Supplier<U> supplier)
// 可以指定线程池  
static CompletableFuture<Void>runAsync(Runnable runnable, Executor executor)
static <U> CompletableFuture<U>supplyAsync(Supplier<U> supplier, Executor executor)  
  • 前两个方法使用默认线程池,它们之间的区别是:Runnable 接口的 run() 方法没有返回值,而 Supplier 接口的 get() 方法是有返回值的。
  • 后两个方法则可以指定线程池参数,成员变量只多了Executor executor,默认情况下会使用ForkJoinPool线程池
  • 创建完 CompletableFuture 对象之后,会自动地异步执行
    runnable.run() 方法或者 supplier.get() 方法

对于一个异步操作,你需要关注两个问题:

  1. 一个是异步操作什么时候结束,
  2. 另一个是如何获取异步操作的执行结果

由于CompletableFuture 类实现了 Future 接口,直接通过Future接口就可以解决了

P.S. ForkJoinPool线程池是一种更智能的线程池,支持Fork/Join的并行计算(类似单机版的MapReduce),利用生产者消费者模型实现,并持“任务窃取“机制

如何理解 CompletionStage 接口

CompletionStage 接口可以清晰地描述任务之间的时序关系,如串行,并行,汇聚(等待两个任务执行都完,才能执行总任务,也可以用CyclicBarrier实现)
f3 = f1.thenCombine(f2, ()->{}) 描述的就是一种汇聚关系,语义很清晰,f3需要等待f1,和 f2执行完成之后才执行

1. 描述串行关系

CompletionStage<R> thenApply(fn);
CompletionStage<R> thenApplyAsync(fn);
CompletionStage<Void> thenAccept(consumer);
CompletionStage<Void> thenAcceptAsync(consumer);
CompletionStage<Void> thenRun(action);
CompletionStage<Void> thenRunAsync(action);
CompletionStage<R> thenCompose(fn);
CompletionStage<R> thenComposeAsync(fn);
CompletableFuture<String> f0 = 
  CompletableFuture.supplyAsync(
    () -> "Hello World")      //①
  .thenApply(s -> s + " QQ")  //②
  .thenApply(String::toUpperCase);//③
 
System.out.println(f0.join());
// 输出结果
HELLO WORLD QQ

2.描述 AND 汇聚关系

CompletionStage<R> thenCombine(other, fn);
CompletionStage<R> thenCombineAsync(other, fn);
CompletionStage<Void> thenAcceptBoth(other, consumer);
CompletionStage<Void> thenAcceptBothAsync(other, consumer);
CompletionStage<Void> runAfterBoth(other, action);
CompletionStage<Void> runAfterBothAsync(other, action);

3.描述 OR 汇聚关系

CompletionStage applyToEither(other, fn);
CompletionStage applyToEitherAsync(other, fn);
CompletionStage acceptEither(other, consumer);
CompletionStage acceptEitherAsync(other, consumer);
CompletionStage runAfterEither(other, action);
CompletionStage runAfterEitherAsync(other, action);

4. 异常处理

fn、consumer、action 它们的核心方法都不允许抛出可检查异常,但是却无法限制它们抛出运行时异常,例如执行 7/0 就会出现除零错误这个运行时异常。非异步编程里面,我们可以使用 try{}catch{}来捕获并处理异常,那在异步编程里面,异常该如何处理呢?

CompletionStage 接口给我们提供的方案非常简单,比 try{}catch{}还要简单,下面是相关的方法,使用这些方法进行异常处理和串行操作是一样的,都支持链式编程方式

CompletionStage exceptionally(fn);
CompletionStage<R> whenComplete(consumer);
CompletionStage<R> whenCompleteAsync(consumer);
CompletionStage<R> handle(fn);
CompletionStage<R> handleAsync(fn);

使用 exceptionally() 方法来处理异常

CompletableFuture<Integer> 
  f0 = CompletableFuture
    .supplyAsync(()->7/0))
    .thenApply(r->r*10)
    .exceptionally(e->0);
System.out.println(f0.join());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天坑转码徐小虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值