并发工具类:异步神器CompletableFuture

在这里插入图片描述

为什么要搞一个CompletableFuture工具类

正好上次分享了函数式接口和Stream的使用,这次就分享一下CompletableFuture,里面也用到了大量的函数式接口

想方便的异步执行任务,就必须放到单独的线程中。继承Thread类,实现Runnable都不能拿到任务的执行结果,这时就不得不提创建线程的另一种方式了,实现Callable接口。

对于简单的场景使用Future并没有什么不方便。但是一些复杂的场景就很麻烦,
如2个异步任务,其中一个有结果就直接返回。Future用起来就不方便,因为想获取结果时,要么执行future.get()方法,但是这样会阻塞线程,变成同步操作,要么轮询isDone()方法,但是比较耗费CPU资源。

Netty和Google guava为了解决这个问题,在Future的基础上引入了观察者模式(即在Future上addListener),当计算结果完成时通知监听者。

Java8新增的CompletableFuture则借鉴了Netty等对Future的改造,简化了异步编程的复杂性,并且提供了函数式编程的能力

在这里插入图片描述
在这里插入图片描述

串行关系

And 汇聚关系

Or汇聚关系

创建CompletableFuture对象

方法名描述
completedFuture(U value)返回一个已经计算好的CompletableFuture
runAsync(Runnable runnable)使用ForkJoinPool.commonPool()作为线程池执行任务,没有返回值
runAsync(Runnable runnable, Executor executor)使用指定的线程池执行任务,没有返回值
supplyAsync(Supplier<U> supplier)使用ForkJoinPool.commonPool()作为线程池执行任务,有返回值
supplyAsync(Supplier<U> supplier, Executor executor)使用指定的线程池执行任务,有返回值
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

Supplier在《强大的Stream》中已经介绍过了,是一个能获取返回值的函数式接口

CompletableFuture<Integer> intFuture = CompletableFuture.completedFuture(100);
// 100
System.out.println(intFuture.get());

CompletableFuture<Void> voidFuture = CompletableFuture.runAsync(() -> System.out.println("hello"));
// null
System.out.println(voidFuture.get());

CompletableFuture<String> stringFuture = CompletableFuture.supplyAsync(() -> "hello");
// hello
System.out.println(stringFuture.get());

计算结果完成时

方法名描述|
whenComplete(BiConsumer<? super T,? super Throwable> action)
whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)

因为入参是BiConsumer<? super T,? super Throwable>函数式接口,所以可以处理正常和异常的计算结果

whenComplete和whenCompleteAsync的区别如下

  1. whenComplete:执行完当前任务的线程继续执行whenComplete的任务
  2. whenCompleteAsync:把whenCompleteAsync这个任务提交给线程池来执行

CompletableFuture的所有方法的定义和whenComplete都很类似

  1. 方法不以Async结尾意味着使用相同的线程执行
  2. 方法以Async结尾意味着将任务提交到线程池来执行
  3. 方法以Async结尾时可以用ForkJoinPool.commonPool()作为线程池,也可以使用自己的线程池

后续介绍的所有方法都只写一种case

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return "hello";
}).whenComplete((v, e) -> {
    // hello
    System.out.println(v);
});
// hello
System.out.println(future.get());

转换,消费,执行

方法名描述
thenApply获取上一个任务的返回,并返回当前任务的值
thenAccept获取上一个任务的返回,单纯消费,没有返回值
thenRun上一个任务执行完成后,开始执行thenRun中的任务
CompletableFuture.supplyAsync(() -> {
    return "hello ";
}).thenAccept(str -> {
    // hello world
    System.out.println(str + "world");
}).thenRun(() -> {
    // task finish
    System.out.println("task finish");
});

组合(两个任务都完成)

方法名描述
thenCombine组合两个future,获取两个future的返回结果,并返回当前任务的返回值
thenAcceptBoth组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值
runAfterBoth组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return "欢迎关注 ";
}).thenApply(t -> {
    return t + "微信公众号 ";
}).thenCombine(CompletableFuture.completedFuture("Java识堂"), (t, u) -> {
    return t + u;
}).whenComplete((t, e) -> {
    // 欢迎关注 微信公众号 Java识堂
    System.out.println(t);
});

组合(只需要一个任务完成)

方法名描述
applyToEither两个任务有一个执行完成,获取它的返回值,处理任务并返回当前任务的返回值
acceptEither两个任务有一个执行完成,获取它的返回值,处理任务,没有返回值
runAfterEither两个任务有一个执行完成,不需要获取future的结果,处理任务,也没有返回值
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "欢迎关注微信公众号";
});
CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "Java识堂";
});
CompletableFuture future = future1.applyToEither(future2, str -> str);
// 欢迎关注微信公众号 Java识堂 随机输出
System.out.println(future.get());

sleepRandom()为我写的一个随机暂停的函数

多任务组合

方法名描述
allOf当所有的CompletableFuture完成后执行计算
anyOf任意一个CompletableFuture完成后执行计算

allOf的使用

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "欢迎关注";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "微信公众号";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "Java识堂";
});
// 欢迎关注 微信公众号 Java识堂
CompletableFuture.allOf(future1, future2, future3)
        .thenApply(v ->
                Stream.of(future1, future2, future3)
                        .map(CompletableFuture::join)
                        .collect(Collectors.joining(" ")))
        .thenAccept(System.out::print);

anyOf的使用

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "欢迎关注";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "微信公众号";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    sleepRandom();
    return "Java识堂";
});
CompletableFuture<Object> resultFuture = CompletableFuture.anyOf(future1, future2, future3);
// 欢迎关注 微信公众号 Java识堂 随机输出
System.out.println(resultFuture.get());

异常处理

方法名描述
exceptionally捕获异常,进行处理
completeExceptionally获取结果时抛出指定异常
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    return 100 / 0;
}).thenApply(num -> {
    return num + 10;
}).exceptionally(throwable -> {
    return 0;
});
// 0
System.out.println(future.get());

当然有一些接口能捕获异常

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    String str = null;
    return str.length();
}).whenComplete((v, e) -> {
    if (e == null) {
        System.out.println("正常结果为" + v);
    } else {
        // 发生异常了java.util.concurrent.CompletionException: java.lang.NullPointerException
        System.out.println("发生异常了" + e.toString());
    }
});
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(new RuntimeException());
// 抛出异常 java.util.concurrent.ExecutionException: java.lang.RuntimeException
Object object = future.get();

参考博客

[0]https://www.shuzhiduo.com/A/6pdD2jRyJw/
[1]https://colobu.com/2016/02/29/Java-CompletableFuture/
[2]https://www.shuzhiduo.com/A/gGdXkYB154/
好的系列文章
[1]https://www.jianshu.com/p/dff9063e1ab6
[2]https://www.jianshu.com/p/d81cf0beb858
[3]https://www.jianshu.com/p/807e6822292a
系列文章
[1]https://juejin.im/post/6844903505052827661
[2]https://juejin.im/post/6844903505057021966
[3]https://juejin.im/post/6844903505057202190
好文
[4]https://www.cnblogs.com/fingerboy/p/9948736.html

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java识堂

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

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

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

打赏作者

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

抵扣说明:

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

余额充值