Java中CompletableFuture 的详细介绍

目录
  • 1.概述
    • 1.0 创建 CompletableFuture 的对象的工厂方法
    • 1.1 non-async 和 async 区别
      • 1.1.1 non-async 示例:注册 action 的时候任务可能已经结束
      • 1.1.2 non-async 示例:注册 action 的时候任务未完成
    • 1.2 Run 类方法
    • 1.3 Accept 类方法
    • 1.4 Apply 类方法
  • 2 单个任务执行完成后执行一个动作(action)
    • 2.0 示例 exceptionally
  • 3 两个任务执行编排
  • 4 多个任务执行编排
  • 5 CompletableFuture 其他方法

1.概述

1.0 创建 CompletableFuture 的对象的工厂方法

static CompletableFuture<Void> runAsync(Runnable runnable)
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

runAsync 的参数是 Runnable, 返回值是 CompletableFuture, 意思是工厂方法创建的 CompletableFuture 对象封装的任务没有返回值。

例如:

CompletableFuture<Void> run = CompletableFuture.runAsync(()-> System.out.println("hello"));

而 suppyAsync 参数类型是 Supplier,返回值是CompletableFuture<U> , 意思是任务不接受参数,但是会返回结果。

CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
    System.out.println("Hello");
    return "hello world!"";
});
System.out.println(supply.get());  //hello world!"

所以如果任务需要返回结果,那么应该选择 suppyAsync;否则可以选择 runAsync。

1.1 non-async 和 async 区别

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

CompletableFuture 中有众多类似这样的方法,那么 non-async 和 async 和版本的方法究竟有什么区别呢? 参考官方文档的描述:

Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.

翻译:传递给 non-async 方法作为参数的函数(action)可能会在完成当前的 CompletableFuture 的线程中执行,也可能会在方法的调用者线程中执行。

All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interface CompletableFuture.AsynchronousCompletionTask.

翻译:所有没有Executor 参数的 async 方法都在 ForkJoinPool.commonPool()线程池中执行(除非不支持最小并发度为2,这种情况下,每个任务会创建一个新线程去执行)。为了简化监控、调试和追踪,所有生成的异步任务都是接口 CompletableFuture.AsynchronousCompletionTask的实例。

从上面这两段官方描述看。async 类方法比较容易理解,就是 CompletableFuture 实例的任务执行完成后,会将 action 提交到缺省的异步线程池 ForkJoinPool.commonPool(),或者 async 类方法参数Executor executor 指定的线程池中执行。

而对于 non-async 的描述则有点不明确。action 可能会在完成 CompletableFuture 实例任务的线程中执行,也可能会在调用 thenRun(编排任务完成后执行 action 的系列方法) 方法的线程中执行。这个主要是看调用 thenRun 的时候,CompletableFuture 实例的任务是否已经完成。如果没有完成,那么action 会在完成任务的线程中执行。如果任务已经完成,则 action 会在调用thenAccept 等注册方法的线程中执行

1.1.1 non-async 示例:注册 action 的时候任务可能已经结束

@Test
void testThenRunWithTaskCompleted() throws Exception{
    CompletableFuture<Void> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            System.out.println("[" + Thread.currentThread().getName() + "] " + " in task" );
            return 1;
        }
    }).thenRun(() -> {
        System.out.println("[" + Thread.currentThread().getName() + "] " + " in action" );
    });
    future.get();
}

运行结果:

[ForkJoinPool.commonPool-worker-1]  in task
[main]  in action

分析: 任务通过 CompletableFuture.supplyAsync 提交后,会以异步的方式在 ForkJoinPool.commonPool() 线程池中运行。这时候有两个线程,一个是[ForkJoinPool.commonPool-worker-1] 执行 Supplier.get 方法;一个是[main] 主线程提交完异步任务后,继续调用 thenRun 编排任务完成后执行 action。由于Supplier.get 非常简单,几乎立刻返回。所以很大概率在主线程调用 thenRun 编排任务完成后执行 action的时候,异步任务已经完成,所以 action 在主线程中执行了。注:在笔者的电脑上几乎100% 是这样的调度方式。

1.1.2 non-async 示例:注册 action 的时候任务未完成

@Test
void testThenRunWithTaskUncompleted() throws Exception{
    CompletableFuture<Void> future = CompletableFuture.supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            System.out.println("[" + Thread.currentThread().getName() + "] " + " in task" );
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return new Random().nextInt(10);
        }
    }).thenRun(() -> {
        System.out.println("[" + Thread.currentThread().getName() + "] " + " in action" );
    });
    future.get();
}

运行结果:

[ForkJoinPool.commonPool-worker-1]  in task
[ForkJoinPool.commonPool-worker-1]  in action

分析:在 Supplier.get 加入 sleep,延迟任务结束。主线程提交完异步任务后,继续调用 thenRun 编排任务完成后执行 action 的时候,任务没有结束。所以这个action 在完成任务的线程中执行。

1.2 Run 类方法

Run 类方法指 thenRun、runAfterBoth、runAfterEither 等。Run 类方法的动作参数都是Runable action。例如 thenRun(Runnable action)。这就意味着这个 action 不关心任务的结果,action 本身也没有返回值。只是为了实现在完成任务后执行一个动作的目的

1.3 Accept 类方法

Accept 类方法指 thenAccept、runAcceptBoth、runAcceptEither 等。Accept 类方法的动作参数都是Consumer<? super T> action。例如 thenAccept(Consumer<? super T> action)。如方法名和参数所示,action 是接受并消费任务结果的消费者,action 没有返回值

1.4 Apply 类方法

Apply 类方法指 thenApply、applyToEither 等。Apply 类方法的动作参数都是Function<? super T,? extends U> fn。例如 thenApply(Function<? super T,? extends U> fn)。如方法名和参数所示,action 将任务结果应用函数参数 fn 做转换,返回转换后的结果,类似于 stream 中的 map

2 单个任务执行完成后执行一个动作(action)

方法 说明
public CompletableFuture<Void> thenRun(Runnable action)
thenRunAsync(Runnable action)
thenRunAsync(Runnable action, Executor executor)
任务完成后执行 action,action 中不关心任务的结果,action 也没有返回值
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
thenAcceptAsync(Consumer<? super T> action)
thenAcceptAsync(Consumer<? super T> action)
任务完成后执行 action。如方法名和参数所示,action 是接受并消费任务结果的消费者,action 没有返回值
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
thenApplyAsync(Function<? super T,? extends U> fn)
thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
任务完成后执行 action。action 将任务结果应用 action 的函数参数做转换,返回转换后的结果,类似于 stream 中的 map
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
任务完成后或者异常时运行action。action 是接受并消费任务结果的消费者,并且有返回值。可以认为是支持异常处理的 thenAccept 版本。
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
任务完成后或者异常时运行action。可以认为是支持异常处理的 thenApply 版本。
public CompletableFuture exceptionally(Function<Throwable, ? extends T> fn) 如果任务或者 action 发生异常,则会触发exceptionally的调用相当于 try...catch

2.0 示例 exceptionally

@Test
void testCompletableFutureExceptionally(){
    CompletableFuture<Integer> first = CompletableFuture
            .supplyAsync(() -> {
                if (true) {
                    throw new RuntimeException("exception in task");
                }
                return "hello world";
            })
            .thenApply(data -> {
                if (true) {
                    throw new RuntimeException("exception in action");
                }
                return 1;
            })
            .exceptionally(e -> {
                System.out.println("[" + Thread.currentThread().getName() + "] " + " print exception stack trace" );
                e.printStackTrace(); // 异常捕捉处理,前面两个处理环节的日常都能捕获
                return 0;
            });
}

3 两个任务执行编排

下面表格列出的每个方法都有对应两个版本的 async 方法,一个有executor 参数,一个没有。为了表格尽量简洁,表格中就不再列出async 方法了

方法 说明
public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage> fn) 串行组合两个 CompletableFuture 任务,后一个任务依赖前一个任务的结果,后一个任务可以返回与第一个任务不同类型的返回值。执行后一个任务的线程与前面讨论 action 执行的线程类似。
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action) 这里有两个并行任务,一个是runAfterBoth 调用本身所属的CompletableFuture 实例A,一个是参数 other 引用的任务B。两个并行任务都完成后执行 action,action 中不关心任务的结果,action 也没有返回值
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) 与runAfterBoth 类似,也有两个并行任务 A 和 B。两个并行任务都完成后执行 action。如方法名和参数所示,action 是接受并消费两个任务结果的消费者,action 没有返回值
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) 与runAfterBoth 类似,也有两个并行任务 A 和 B。两个并行任务都完成后执行 action,action 依赖两个任务的结果,并对结果做转换,返回一个新值
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action) 与runAfterBoth 类似,也有两个并行任务 A 和 B。两个并行任务任意一个完成后执行 action,action 中不关心任务的结果,action 也没有返回值
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) 与runAfterBoth 类似,也有两个并行任务 A 和 B。两个并行任务任意一个完成后执行 action。如方法名和参数所示,action 是接受并消费两个任务结果的消费者,action 没有返回值
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) 与runAfterBoth 类似,也有两个并行任务 A 和 B。两个并行任务任意一个完成后执行 action。action 将任务结果应用 action 的函数参数做转换,返回转换后的结果,类似于 stream 中的 map

4 多个任务执行编排

下面表格列出的每个方法都有对应两个版本的 async 方法,一个有executor 参数,一个没有。为了表格尽量简洁,表格中就不再列出async 方法了

方法 说明
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) 所有参数引用的任务都完成后,才触发执行当前的 CompletableFuture 实例任务。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) 参数引用的任务中任何一个完成后,就会触发执行当前的 CompletableFuture 实例任务。

5 CompletableFuture 其他方法

方法 说明
public boolean cancel(boolean mayInterruptIfRunning) 如果任务未完成,以 CancellationException 异常结束任务。
public boolean isCancelled() 判断任务是否取消。
public T join() 阻塞等待 获取返回值
public T get() throws InterruptedException, ExecutionException
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
阻塞等待(有超时重载版本)获取返回值。get 与 join区别,get 会抛出 checked 异常,调用代码需要处理异常。join 没有超时重载版本。
public T getNow(T valueIfAbsent) 获取返回值,如果任务未完成则返回valueIfAbsent 参数指定value
public boolean isDone() 任务是否执行完成

到此这篇关于Java中CompletableFuture 的详细介绍的文章就介绍到这了,更多相关CompletableFuture 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java8通过CompletableFuture实现异步回调

    目录 1 什么是CompletableFuture? 2 为什么会有CompletableFuture ? 3 CompletableFuture 简单使用 4 CompletableFuture 源码分析 4.1 创建异步任务 4.2 异步任务回调 4.3 异步任务组合 前言: java5为我们提供了Callable和Future,使我们可以很容易的完成异步任务结果的获取,但是通过Future的get获取异步任务结果会导致主线程的阻塞,这样在某些场景下是非常消耗CPU资源的,进而Java8为我

  • Java8中CompletableFuture使用场景与实现原理

    目录 1.概述 2.为什么引入CompletableFuture 3.功能 3.源码追踪 4.总结 1.概述 CompletableFuture是jdk1.8引入的实现类.扩展了Future和CompletionStage,是一个可以在任务完成阶段触发一些操作Future.简单的来讲就是可以实现异步回调. 2.为什么引入CompletableFuture 对于jdk1.5的Future,虽然提供了异步处理任务的能力,但是获取结果的方式很不优雅,还是需要通过阻塞(或者轮训)的方式.如何避免阻塞呢?

  • 详解Java8 CompletableFuture的并行处理用法

    目录 前言 场景 用法 1.在线API 2.编写在线API查询 3.编写查询服务 4.编写测试接口 5.效果 6.CompletableFuture并行查询 7.编写测试接口 8.CompletableFuture效果 思考 前言 工作中你可能会遇到很多这样的场景,一个接口,要从其他几个service调用查询方法,分别获取到需要的值之后再封装数据返回. 还可能在微服务中遇到类似的情况,某个服务的接口,要使用好几次feign去调用其他服务的方法获取数据,最后拿到想要的值并封装返回给前端. 这样的场

  • 详解Java CompletableFuture使用方法以及与FutureTask的区别

    目录 futureTask 创建异步任务 创建任务 1. .supplyAsync 2. .runAsync 异步回调 1. .thenApply 2. .thenAccept 3. .exceptionally 4. .whenComplete 组合处理 总的来说简洁了FutureTask与线程池的配合使用 没啥太大区别吧我觉得, 使用方法不一样, 多了一些方法 ??? futureTask 创建异步任务 FutureTask<String> stringFutureTask = new F

  • Java8 使用工厂方法supplyAsync创建CompletableFuture实例

    目录 使用工厂方法 supplyAsync创建 CompletableFuture 对比 对CompletableFuture async的理解 目前为止我们已经了解了如何通过编程创建 CompletableFuture 对象以及如何获取返回值,虽然看起来这些操作已经比较方便,但还有进一步提升的空间, CompletableFuture 类自身提供了大量精巧的工厂方法,使用这些方法能更容易地完成整个流程,还不用担心实现的细节. 可以看到我们使用new Thread的方式,显然是不恰当的. 使用工

  • Java8 自定义CompletableFuture的原理解析

    目录 Java8 自定义CompletableFuture原理 CompleteFuture简单使用 下面简单介绍用法 Java8 自定义CompletableFuture原理 Future 接口 的局限性有很多,其中一个就是需要主动的去询问是否完成,如果等子线程的任务完成以后,通知我,那岂不是更好? public class FutureInAction3 { public static void main(String[] args) { Future<String> future = i

  • 深入学习java8 中的CompletableFuture

    目录 1 前言 2 简单使用 3 异步处理 3.1 thenApply 3.2 thenAccept 和 thenRun 3.3 exceptionally 异常处理 3.4 whenComplete 方法完成之后 3.5 handle 4 处理组合 4.1 任务均完成后组合 4.2 任一任务完成 4.3 任务处理结果 4.4 所有或者任何 5 总结 1 前言 在项目开发中,异步化处理是非常常见的解决问题的手段,异步化处理除了使用线程池之外,还可以使用 CompletableFuture 来实现

  • Java中CompletableFuture 的详细介绍

    目录 1.概述 1.0 创建 CompletableFuture 的对象的工厂方法 1.1 non-async 和 async 区别 1.1.1 non-async 示例:注册 action 的时候任务可能已经结束 1.1.2 non-async 示例:注册 action 的时候任务未完成 1.2 Run 类方法 1.3 Accept 类方法 1.4 Apply 类方法 2 单个任务执行完成后执行一个动作(action) 2.0 示例 exceptionally 3 两个任务执行编排 4 多个任

  • Java中ArrayList类详细介绍

    Java中ArrayList类详细介绍 ArrayList是一个可变长度数组,它实现了List接口,因此它也可以包含重复元素和Null元素,也可以任意的访问和修改元素,随着向 ArrayList 中不断添加元素,其容量也自动增长.不过ArrayList是非同步(同步的意思是如果多个线程同时访问一个实例,任何一个线程对实例做了修改之后,其他线程所访问到的实例应该是修改过的最新的实例)的, 我们经常使用List list = Collections.synchronizedList(new Arra

  • JAVA中Context的详细介绍和实例分析

    最熟悉的陌生人--Context 刚刚学android或者js等,都会看见这个频繁的字眼--Context. 意为"上下文". 本文主要记述,Context到底是什么.如何理解Context.一个APP可以有几个Context.Context能干啥.Context的作用域.获取Context.全局获取Context技巧. 思考: Java:万物皆对象.Flutter:万物皆组件. 俗语:"没对象吗?自己new一个啊~" 既然大多数情况可以new一个实例,那么,我们在

  • Java中的参数传递详细介绍

    目录 前言 1.值传递 2.引用传递 3.String类型传递 4.举例 总结 前言 Java中的参数传递:分为值传递和引用传递但本质上,Java中只有值传递.引用传递,其实可以理解为传的是类似指针的东西.值传递就是把基本变量的值拷贝一份,传递这个拷贝.引用传递则是传递的引用的地址,也就是该变量在内存空间的地址. 1.值传递 只有基本数据类型采用值传递,特点是传递的是值的拷贝,传递完后两者就没有关系了.也就是说方法内和方法外的值互不相干 基本数据类型:·整型:int,long,byte,shor

  • 从java中调用matlab详细介绍

    前段时间摸索了java调用matlab东西,不说学的有多深,也算有结果了,达到目的了.也即用java程序可以调用matlab中函数了. 按顺序说吧,最开始肯定是下个matlab看看.下哪一个呢.开始下一个7.0觉得很新了.后来才觉得不是,现在都有7.8了.同时网上还流传着另外一种版本号信息,即2006版. 2006a版.2008b版,后来从网上找资料才知道,原来,matlab是一年两版的,叫a版和b 版.比如MATLAB 7.2 (Release 2006a),我下的是m7.8即2009a版.网

  • Java编程中的构造函数详细介绍

    本文主要是为新手.对java语言感兴趣的人和那些没有系统学习过java基础知识的人进行一个总结,在文章中对构造函数进行了较为详细的说明和讨论,也包含了我个人对于java面向对象中构造函数的一些看法.希望走在java学习道路上的同行者可以有一个较为清晰的认知和理解.当然仅为个人观点,水平有限,不足之处,还请大家多多指出,互相交流学习. 1.构造函数的概念 很多java新手谈到构造函数就会犯晕,我们先来看看什么是构造函数. 首先,构造函数是函数的一种特殊形式,特殊在哪里?构造函数中不需要定义返回类型

  • maven中pom.xml详细介绍

    POM 代表工程对象模型.它是使用 Maven 工作时的基本组建,是一个 xml 文件.它被放在工程根目录下,文件命名为 pom.xml. POM 包含了关于工程和各种配置细节的信息,Maven 使用这些信息构建工程. POM 也包含了目标和插件.当执行一个任务或者目标时,Maven 会查找当前目录下的 POM,从其中读取所需要的配置信息,然后执行目标.能够在 POM 中设置的一些配置如下: project dependencies plugins goals build profiles pr

  • java并发之ArrayBlockingQueue详细介绍

    java并发之ArrayBlockingQueue详细介绍 ArrayBlockingQueue是常用的线程集合,在线程池中也常常被当做任务队列来使用.使用频率特别高.他是维护的是一个循环队列(基于数组实现),循环结构在数据结构中比较常见,但是在源码实现中还是比较少见的. 线程安全的实现 线程安全队列,基本是离不开锁的.ArrayBlockingQueue使用的是ReentrantLock,配合两种Condition,实现了集合的线程安全操作.这里稍微说一个好习惯,下面是成员变量的声明. pri

  • java数据类型与二进制详细介绍

    java数据类型与二进制详细介绍 在java中 Int 类型的变量占 4个字节 Long 类型的变量占8个字节 一个程序就是一个世界,变量是这个程序的基本单位. Java基本数据类型 1.        整数类型 2.        小数(浮点数)类型 3.        布尔类型 4.        字符类型 整数类型 整数类型可以表示一个整数,常用的整数类型有:byte,short,int,long Byte  一个字节  -128到127 注:0有两个表示0000 0000正零  1000

  • Java类加载基本过程详细介绍

    Java类加载基本过程详细介绍 基本过程: 根据类的全限定名称加载定义类的二进制字节流. 将字节流代表的静态存储结构转化为方法区的运行时数据结构 内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据访问入口 数组类本身不通过类加载器创建,由java虚拟机直接创建,数组类的元素类型由类加载器加载. 数组类的元素类型:数组去掉所有维度后的类型, 文件格式验证: 0xCAFEBABY 魔数开头: 主次版本号当前虚拟机可处理: 常量类型: 索引执行类型: utf8编码

随机推荐