Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >异步神器CompletableFuture

异步神器CompletableFuture

作者头像
Java识堂
发布于 2020-09-14 08:58:47
发布于 2020-09-14 08:58:47
1.2K00
代码可运行
举报
文章被收录于专栏:Java识堂Java识堂
运行总次数:0
代码可运行

介绍

上个礼拜我们线上有个接口比较慢,这个接口在刚开始响应时间是正常的。但随着数据量的增多,响应时间变慢了。

这个接口里面顺序调用了2个服务,且2个服务之间没有数据依赖。我就用CompletableFuture把调用2个服务的过程异步化了一下,响应时间也基本上缩短为原来的一半,问题解决。

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Callable接口一般配合ExecutorService来使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ExecutorService.java
<T> Future<T> submit(Callable<T> task);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> result = executor.submit(() -> {
    int sum = 0;
    for (int i = 0; i < 100; i++) {
        sum += i;
    }
    return sum;
});
// 4950
System.out.println(result.get());

我们从Future中获取结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}

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

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

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

创建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)

使用指定的线程池执行任务,有返回值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return "hello";
}).whenComplete((v, e) -> {
    // hello
    System.out.println(v);
});
// hello
System.out.println(future.get());

转换,消费,执行

方法名

描述

thenApply

获取上一个任务的返回,并返回当前任务的值

thenAccept

获取上一个任务的返回,单纯消费,没有返回值

thenRun

上一个任务执行完成后,开始执行thenRun中的任务

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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处理完任务后,处理该任务

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的结果,处理任务,也没有返回值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

捕获异常,进行处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    return 100 / 0;
}).thenApply(num -> {
    return num + 10;
}).exceptionally(throwable -> {
    return 0;
});
// 0
System.out.println(future.get());

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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());
    }
});
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java识堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java8新的异步编程方式 CompletableFuture(二)
上一篇文章,讲述了Future模式的机制、缺点,CompletableFuture产生的由来、静态工厂方法、complete()方法等等。
fengzhizi715
2018/08/24
1.4K0
Java8新的异步编程方式 CompletableFuture(三)
前面两篇文章已经整理了CompletableFuture大部分的特性,本文会整理完CompletableFuture余下的特性,以及将它跟RxJava进行比较。
fengzhizi715
2018/08/24
2.5K0
【第十四篇】商城系统-异步处理利器-CompletableFuture
  上面的三种获取线程的方法是直接获取,没有对线程做相关的管理,这时可以通过线程池来更加高效的管理线程对象。
用户4919348
2022/10/04
3790
【第十四篇】商城系统-异步处理利器-CompletableFuture
【Java多线程-5】 CompletableFuture详解
Future 是Java 5添加的类,用来描述一个异步计算的结果。前文中,我们领略了 Future 的便利,但它还是存在诸多不足,比如:
云深i不知处
2020/09/16
4K0
【小家java】Java8新特性之---CompletableFuture的系统讲解和实例演示(使用CompletableFuture构建异步应用)
传统单线程环境下,调用函数是同步的,必须等待程序返回结果后,才可进行其他处理。因此为了提高系统整体的并发性能,引入了异步执行~
YourBatman
2019/09/03
3.9K0
【小家java】Java8新特性之---CompletableFuture的系统讲解和实例演示(使用CompletableFuture构建异步应用)
CompletableFuture使用详解
在上一篇文章《CompletionService使用与源码分析》中,已经介绍过了Future的局限性,它没法直接对多个任务进行链式、组合等处理,需要借助并发工具类才能完成,实现逻辑比较复杂。
全栈程序员站长
2022/09/07
9350
CompletableFuture使用详解
CompletableFuture异步回调
  Future模式是高并发设计与开发过程中常见的设计模式,它的核心思想是异步调用。对于Future模式来说,它不是立即返回我们所需要的数据,但是它会返回一个契约(或异步任务),将来我们可以凭借这个契约(或异步任务)获取需要的结果。
别团等shy哥发育
2023/10/17
5510
CompletableFuture异步回调
CompletableFuture 使用详解
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。
java404
2018/10/10
4.1K1
CompletableFuture实现异步编排
场景:电商系统中获取一个完整的商品信息可能分为以下几步:①获取商品基本信息 ②获取商品图片信息 ③获取商品促销活动信息 ④获取商品各种类的基本信息 等操作,如果使用串行方式去执行这些操作,假设每个操作执行1s,那么用户看到完整的商品详情就需要4s的时间,如果使用并行方式执行这些操作,可能只需要1s就可以完成。所以这就是异步执行的好处。
科技新语
2023/02/01
1.6K0
CompletableFuture学习
前面我们已经知道CompletionService是可以解决Future带来的阻塞问题的,同时我们除了前面我们看到的take方法之外,还可以使用poll方法,这样可以使你的程序免受阻塞之苦。因为poll方法也是无阻塞性的。同时在kafka的源码中,我们如果使用消费者的话,可以看到会使用一个基于future的poll方法。同时我们可以在dubbo的新版本2.7中,可以看到其异步编程采用的就是我们要介绍的CompletableFuture。因此,我们有必要了解CompletableFuture,同时其也是真正意义上的异步编程的实现。
路行的亚洲
2020/07/16
1.1K0
Java CompletableFuture 详解
Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel方法停止任务的执行。
Erwin
2021/04/19
1.6K0
CompletableFuture深度解析
在异步编程中,我们经常需要处理各种异步任务和操作。Java 8引入的 CompletableFuture 类为我们提供了一种强大而灵活的方式来处理异步编程需求。CompletableFuture 类提供了丰富的方法和功能,能够简化异步任务的处理和组合。
BookSea
2024/01/11
4950
CompletableFuture深度解析
CompletableFuture Java多线程操作
CompletableFuture是Java8中新增加的类,结合了Future的优点,提供了非常强大的Future的拓展功能,帮助简化了异步编程的复杂性。
摸鱼的G
2023/02/22
7280
【并发编程】异步编程CompletableFuture实战
在JDK8之前,我们使用的Java多线程变成,主要是 Thread+Runnable 来完成,但是这种方式有个弊端就是没有返回值。如果想要返回值怎么办呢,大多数人就会想到 Callable + Thread 的方式来获取到返回值。
互联网小阿祥
2023/05/28
1.1K0
【并发编程】异步编程CompletableFuture实战
死磕Juc(一)之CompletableFuture
Future接口定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任
yuanshuai
2022/08/17
5600
死磕Juc(一)之CompletableFuture
Future系列(CompletableFuture的使用)
CompletableFuture提供了四个静态方法用来创建CompletableFuture对象:
提莫队长
2021/03/09
6950
CompletableFuture 应用实践
首先来看一个问题,如果要执行多个任务,每个任务会返回对应结果,现在需要所有任务执行完毕之后,将这些任务结果统一打印出来,该如何完成呢?注意尽量不要使用业务线程来等待多个任务的结果,也就是不要使用Future.get方式。
luoxn28
2020/12/02
6890
CompletableFuture 应用实践
Java8使用CompletableFuture的部分方法
CompletableFuture的使用是为了异步编程,异步编程可以解决同步编程的性能瓶颈问题。也就是将同步操作变为了并行操作。 当我们有一大批数据需要处理的时候我们可以将这些数据分而治之,使用CompletableFuture通过线程池的多个线程进行异步执行。
袁新栋-jeff.yuan
2020/08/26
1.6K0
JUC从实战到源码:六千字详细学习CompletableFuture
上篇文章,我们学习了Future的基本使用,以及其优缺点,然而其缺点是更加突出的,这也就在jdk8的时候就引申出CompletableFuture,这个类更能够很好的解决了异步编程来使性能提升。然而这是如何从Future演变到CompletableFuture呢?这就是我们这章将要学习的内容。
怒放吧德德
2024/09/19
2840
【JAVA 的异步编程】CompletableFuture的介绍
缘起: 一、Future java5引入了Future模式。Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算。 Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。 Future的接口的五个方法。
用户5640963
2021/06/17
8570
【JAVA 的异步编程】CompletableFuture的介绍
相关推荐
Java8新的异步编程方式 CompletableFuture(二)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验