CompletableFuture学习

学习链接

Object的wait和notify&为什么要先获取锁,才能调用wait/notify&虚假唤醒

Future&FutureTask异步学习

【深度解析】万字长文,一文讲透CompletableFuture:Java异步编程的利器!

文章目录

1. Callable&Future&FutureTask介绍

1.1 背景

直接继承Thread或者实现Runnable接口都可以创建线程,但是这两种方法都有一个问题就是:没有返回值,也就是不能获取执行完的结果。因此java1.5就提供了Callable接口来实现这一场景,而Future和FutureTask就可以和Callable接口配合起来使用。

Runnable 的缺陷:

  • 不能返回一个返回值
  • 不能抛出 checked Exception

Callable的call方法可以有返回值,可以声明抛出异常。和 Callable 配合的有一个 Future 类,通过 Future 可以了解任务执行情况,或者取消任务的执行,还可获取任务执行的结果,这些功能都是 Runnable 做不到的,Callable 的功能要比 Runnable 强大。

Runnable接口

java 复制代码
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
TestRunnable
java 复制代码
public class TestRunnable {

    public static void main(String[] args) {

        testRunnable01();

    }

    private static void testRunnable01() {
        // 1、原理
        //    1. 将1个Runnable接口实例放入1个线程对象中,
        //       调用该线程对象的start()方法,
        //       就可以开启1个线程去执行Runnable接口实现的run方法
        // 2、缺点
        //    1. 不知道这个任务什么时候结束?
        //    2. 任务执行完成不能设置结果?
        //    3. 不能对该任务设定1个超时时间去获取任务结果?
        //    4. 无法中断这个任务?
        //    5. 无法知道这个任务的是否正常执行完成?
        //    6. 无法知道这个任务是否发生异常及发生的异常?
        //    7. 执行任务的线程被销毁没有复用?
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("执行Runnable任务");
            }
        };
        new Thread(task).start();
    }

}

Callable接口

java 复制代码
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}
TestCallable
java 复制代码
@Slf4j
public class TestCallable {

    public static void main(String[] args) {

        testCallable();

    }

    private static void testCallable() {

        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                return "ok";
            }
        };

        // 1. FutureTask实现Runnable接口,可以被当作任务 放到线程中执行
        // 2. FutureTask实现Future接口,可以用来查询任务状态,也可以阻塞获取任务执行结果
        // 3. FutureTask必须要等到任务完全执行完才能获取结果(netty的promise弥补了这点)
        // 4. FutureTask只能阻塞获取结果而不能异步获取任务执行结果(netty的Future弥补了这点)
        // 5. FutureTask不能对多个任务进行编排
        FutureTask<String> futureTask = new FutureTask<>(callable);

        new Thread(futureTask).start();

        try {
            String result1 = futureTask.get(500, TimeUnit.MILLISECONDS);
            log.info("result1 = {}", result1);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            System.out.println("超时了" + e);
        }

        try {
            String result2 = futureTask.get();
            log.info("result2 = {}", result2);
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }

    }

}

1.2 Future 的API

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果

java 复制代码
// 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
boolean cancel(boolean mayInterruptIfRunning);

// 任务是否已经取消,任务正常完成前将其取消,则返回 true
boolean isCancelled();

// 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
boolean isDone();

// 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
V get () throws InterruptedException, ExecutionException ; 

// 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException
V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException ;

最简化的FutureTask实现

java 复制代码
public class FutureTask<V> implements Runnable {

    private V result;
    
    private final Callable<V> callable;
    
    private final Object lock = new Object();
    
    private boolean done = false;  // 添加一个完成标志
    
    public V get() {
        synchronized (lock) {
            while (!done) {  // 等待条件改为 done
                lock.wait();
            }
            return result;  // 即使result为null也返回
        }
    }
    
    @Override
    public void run() {
        synchronized (lock) {
            try {
                result = callable.call();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                done = true;      // 无论如何都标记为完成
                lock.notifyAll(); // 无论如何都通知
            }
        }
    }
}

1.3 FutureTask 使用

Future实际采用FutureTask实现,该对象相当于是消费者和生产者的桥梁,消费者通过 FutureTask 存储任务的处理结果,更新任务的状态:未开始、正在处理、已完成等。而生产者拿到的 FutureTask 被转型为 Future 接口,可以阻塞式获取任务的处理结果,非阻塞式获取任务处理状态。

FutureTask既可以被当做Runnable来执行,也可以被当做Future来获取Callable的返回结果。


把 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,然后把这个对象当作一个 Runnable 对象,放到线程池中或另起线程去执行,最后还可以通过 FutureTask 获取任务执行的结果。

FutureTaskDemo1

java 复制代码
public class FutureTaskDemo1 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        Task task = new Task();
        
        //构建futureTask
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        
        //作为Runnable入参
        new Thread(futureTask).start();

        System.out.println("task运行结果:"+futureTask.get());
    }

    static class Task implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            
            System.out.println("子线程正在计算");
            
            int sum = 0;
            
            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            
            return sum;
        }
    }
}

FutureTaskDemo2

在维护促销活动时需要查询商品信息(包括商品基本信息、商品价格、商品库存、商品图片、商品销售状态等)

这些信息分布在不同的业务中心,由不同的系统提供服务。如果采用同步方式,假设一个接口需要50ms,那么一个商品查询下来就需要200ms-300ms,这对于我们来说是不满意的。如果使用Future改造则需要的就是最长耗时服务的接口,也就是50ms左右。

java 复制代码
public class FutureTaskDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        FutureTask<String> ft1 = new FutureTask<>(new T1Task());
        FutureTask<String> ft2 = new FutureTask<>(new T2Task());
        FutureTask<String> ft3 = new FutureTask<>(new T3Task());
        FutureTask<String> ft4 = new FutureTask<>(new T4Task());
        FutureTask<String> ft5 = new FutureTask<>(new T5Task());

        //构建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        
        executorService.submit(ft1);
        executorService.submit(ft2);
        executorService.submit(ft3);
        executorService.submit(ft4);
        executorService.submit(ft5);
        
        //获取执行结果
        System.out.println(ft1.get());
        System.out.println(ft2.get());
        System.out.println(ft3.get());
        System.out.println(ft4.get());
        System.out.println(ft5.get());

        executorService.shutdown();

    }

    static class T1Task implements Callable<String> {
        
        @Override
        public String call() throws Exception {
            
            System.out.println("T1:查询商品基本信息...");
            
            TimeUnit.MILLISECONDS.sleep(50);
            
            return "商品基本信息查询成功";
        }
    }

    static class T2Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("T2:查询商品价格...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品价格查询成功";
        }
    }

    static class T3Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("T3:查询商品库存...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品库存查询成功";
        }
    }

    static class T4Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("T4:查询商品图片...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品图片查询成功";
        }
    }

    static class T5Task implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println("T5:查询商品销售状态...");
            TimeUnit.MILLISECONDS.sleep(50);
            return "商品销售状态查询成功";
        }
    }

}

1.4 Future的局限性

FutureTask的要点

FutureTask实现了Runnable接口,因此可以作为1个任务提交给线程(池)执行;

FutureTask实现了Future接口,可以获取任务执行结果;

FutureTask组合了Callable接口,可以将Callable接口实例适配为Runnable接口实例。

Future的缺陷

从本质上说,Future表示一个异步计算的结果。它提供了isDone()来检测计算是否已经完成,并且在计算结束后,可以通过get()方法来获取计算结果。在异步计算中,Future确实是个非常优秀的接口。但是,它的本身也确实存在着许多限制:

  • 并发执行多任务:Future只提供了get()方法来获取结果,并且是阻塞的。所以,除了等待你别无他法;
  • 无法对多个任务进行链式调用:如果你希望在计算任务完成后执行特定动作,比如发邮件,但Future却没有提供这样的能力;
  • 无法组合多个任务:如果你运行了10个任务,并期望在它们全部执行结束后执行特定动作,那么在Future中这是无能为力的;
  • 没有异常处理:Future接口中没有关于异常处理的方法;

2. CompletableFuture使用详解

简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的很多时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Future 实现,是非常麻烦的。

CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。

更为重要的是,CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。

2.1 应用场景

描述依赖关系:

  1. thenApply() 把前面异步任务的结果,交给后面的Function
  2. thenCompose()用来连接两个有依赖关系的任务,结果由第二个任务返回

描述and聚合关系:

  1. thenCombine:任务合并,有返回值
  2. thenAccepetBoth:两个任务执行完成后,将结果交给thenAccepetBoth消耗,无返回值。
  3. runAfterBoth:两个任务都执行完成后,执行下一步操作(Runnable)。

描述or聚合关系:

  1. applyToEither:两个任务谁执行的快,就使用那一个结果,有返回值。
  2. acceptEither: 两个任务谁执行的快,就消耗那一个结果,无返回值。
  3. runAfterEither: 任意一个任务执行完成,进行下一步操作(Runnable)。

并行执行:

  • CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行

2.2 创建异步操作

CompletableFuture 提供了四个静态方法来创建一个异步操作:

runAsync

java 复制代码
// 表示创建1个CompletableFuture,并同时关联1个Runnable,
// 并使用异步线程池(forkJoinPool,默认都是守护线程)去执行该Runnable任务,该任务无返回值,
// 当此Runnable执行完(不管是正常执行完,还是执行过程中抛出异常),此时CompletableFuture的状态就是完成状态
public static CompletableFuture<Void> runAsync(Runnable runnable)

// 表示创建1个CompletableFuture,并同时关联1个Runnable,
// 并使用指定的线程池去执行该Runnable任务,该任务无返回值
// 当此Runnable执行完(不管是正常执行完,还是执行过程中抛出异常),此时CompletableFuture的状态就是完成状态
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

supplyAsync

java 复制代码
// 表示创建1个CompletableFuture,并同时关联1个Supplier,
// 并使用异步线程池(forkJoinPool,默认都是守护线程)去执行该Supplier#supplier()方法,该方法有返回值,
// 当此Supplier#supplier()方法执行完(不管是正常执行完,还是执行过程中抛出异常),此时CompletableFuture的状态就是完成状态
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)

// 表示创建1个CompletableFuture,并同时关联1个Supplier,
// 并使用指定的线程池去执行该Supplier#supplier()方法,该方法有返回值,
// 当此Supplier#supplier()方法执行完(不管是正常执行完,还是执行过程中抛出异常),此时CompletableFuture的状态就是完成状态
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

这四个方法区别在于:

  • runAsync 方法以Runnable函数式接口类型为参数,没有返回结果,supplyAsync 方法Supplier函数式接口类型为参数,返回结果类型为U;Supplier 接口的 get() 方法是有返回值的(会阻塞)
  • 没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。注意,forkJoinPool里面的线程默认都是守护线程。
  • 默认情况下 CompletableFuture 会使用公共的 ForkJoinPool 线程池,这个线程池默认创建的线程数是 CPU 的核数(也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置 ForkJoinPool 线程池的线程数)。如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰
java 复制代码
@Slf4j
public class TestCompletableFuture1 {

    public static void main(String[] args) {

        // testRunAsync();
        // testSupplyAsync();
        // testCompletableFutureUsingNoArgsConstructor();
        // testForkJoinPool();

    }

    private static void testForkJoinPool() {
        for (int i = 0; i < 50; i++) {
            final int j  = i;
            CompletableFuture.runAsync(() -> {
                log.info("线程: {}, i: {}", Thread.currentThread().getName(), j);
                log.info("isDaemon: {}", Thread.currentThread().isDaemon());
                try {
                    TimeUnit.SECONDS.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        LockSupport.park();
    }

    private static void testCompletableFutureUsingNoArgsConstructor() {
        CompletableFuture<String> future = new CompletableFuture<>();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            future.complete("hello supplyAsync");
            // 此处返回false, 不会抛出异常
            boolean completeExceptionally = future.completeExceptionally(new RuntimeException("halooooo"));
            log.info("completeExceptionally: {}", completeExceptionally);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("异步通知结果哦");
        }).start();
        log.info("尝试获取结果");
        try {
            String result = future.get();
            log.info("结果: {}", result);
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    private static void testSupplyAsync() {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync...start");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("supplyAsync...end");
            return "hello supplyAsync";
        });
        String result = null;
        try {
            result = future.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
        log.info("supplier result: {}", result);
    }

    private static void testRunAsync() {
        CompletableFuture.runAsync(() -> {
            log.info("runAsync...start");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("runAsync...end");
        });

    }

}

2.3 获取结果

join()和get()方法都是用来获取CompletableFuture异步之后的返回值。

join

join()方法抛出的是uncheck异常(即未经检查的异常),不会强制开发者抛出。

get

get()方法抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)

2.4 结果处理

whenComplete

当CompletableFuture的计算结果完成,或者抛出异常的时候,我们可以执行特定的 Action。

java 复制代码
// 给CompletableFuture实例注册1个已完成状态的BiConsumer回调;
// 当此CompletableFuture已完成时,会执行该BiConsumer回调(无论该任务是正常完成,还是异常完成);
// 如果注册时,该CompletableFuture实例已完成,则由注册线程直接执行回调,若注册时,该CompletableFuture未完成,则完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiConsumer回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiConsumer回调;
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)

// 给CompletableFuture实例注册1个已完成状态的BiConsumer回调;
// 当此CompletableFuture已完成时,会执行该BiConsumer回调(无论该任务是正常完成,还是异常完成);
// 无论注册时该CompletableFuture实例是否已完成,都将在完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiConsumer回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiConsumer回调;
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)

// 给CompletableFuture实例注册1个已完成状态的BiConsumer回调;
// 当此CompletableFuture已完成时,会执行该BiConsumer回调(无论该任务是正常完成,还是异常完成);
// 无论注册时该CompletableFuture实例是否已完成,都将在完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiConsumer回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiConsumer回调;
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
  • Action的类型是BiConsumer<? super T,? super Throwable>,它可以处理正常的计算结果,或者异常情况。
  • 方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
  • 这几个方法都会返回CompletableFuture,当Action执行完毕后它的结果返回原始的CompletableFuture的计算结果或者返回异常
java 复制代码
// 表示创建1个CompletableFuture,并同时关联1个Supplier,
// Supplier实例的supply()方法将被异步线程池(forkJoinPool,默认都是守护线程)异步执行
// 当CompletableFuture内部封装的Supplier实例的supply()方法执行完毕时(无论是正常完成,还是异常完成),此CompletableFuture就是已完成状态(可类比前端的Promise)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    }
    if (new Random().nextInt(10) % 2 == 0) {
        int i = 12 / 0;
    }
    System.out.println("执行结束!");
    return "test";
});

// 给CompletableFuture实例注册1个已完成状态的BiConsumer回调
// 当此CompletableFuture已完成时,会执行该BiConsumer回调(无论该任务是正常完成,还是异常完成)
// 如果注册时,该CompletableFuture实例已完成,则由注册线程直接执行回调,若注册时,该CompletableFuture未完成,则完成时由完成线程执行回调
future.whenComplete(new BiConsumer<String, Throwable>() {
    @Override
    public void accept(String t, Throwable action) {
        System.out.println(t+" 执行完成!");
    }
});

exceptionally

java 复制代码
// 给CompletableFuture实例注册1个异常完成状态的BiConsumer回调;
// 当此CompletableFuture异常完成时,会执行该BiConsumer回调;
// 如果注册时,该CompletableFuture实例已异常完成,则由注册线程直接执行回调,若注册时,该CompletableFuture未完成,则异常完成时由异常完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
java 复制代码
// 给CompletableFuture实例注册1个异常完成状态的BiConsumer回调
// 当此CompletableFuture异常完成时,会执行该BiConsumer回调(无论该任务是正常完成,还是异常完成)
// 如果注册时,该CompletableFuture实例已异常完成,则由注册线程执行回调,若注册时,该CompletableFuture未完成,则异常完成时由完成线程执行回调
future.exceptionally(new Function<Throwable, String>() {
    @Override
    public String apply(Throwable t) {
        System.out.println("执行失败:" + t.getMessage());
        return "异常xxxx";
    }
}).join();

2.5 结果转换

所谓结果转换,就是将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果。

thenApply

把前面任务的执行结果,交给后面的Function

java 复制代码
// 给CompletableFuture实例注册1个结果转换的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenApply中的回调。如果 completableFuture发生异常, 则不会执行thenApply中的回调,并且thenApply()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时已正常执行完,则由注册线程直接执行回调。如果注册时未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);

// 给CompletableFuture实例注册1个结果转换的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenApply中的回调。如果 completableFuture发生异常, 则不会执行thenApply中的回调,并且thenApply()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn);

// 给CompletableFuture实例注册1个结果转换的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenApply中的回调。如果 completableFuture发生异常, 则不会执行thenApply中的回调,并且thenApply()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);

thenCompose

用来连接两个有依赖关系的任务,结果由第二个任务返回

java 复制代码
// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenCompose中的回调。如果 completableFuture发生异常, 则不会执行thenCompose中的回调,并且thenCompose()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时已正常执行完,则由注册线程直接执行回调。如果注册时未执行完,则在正常完成时由完成线程执行回调;
// 在注册Function函数时,Function函数将接收CompletableFuture实例的结果,并且该Function函数将返回1个CompletableFuture实例。这个Function函数返回的CompletableFuture的实例将决定thenCompose()注册时所返回的CompletaleFuture实例的状态。
// 在thenCompose注册时,会返回1个CompletableFuture实例,当Function回调正常执行完毕时,thenCompose注册返回的CompletableFuture实例的状态就取决于Function回调返回的CompletableFuture实例的状态。当Function回调执行过程中发生异常时,thenCompose返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);

// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenCompose中的回调。如果 completableFuture发生异常, 则不会执行thenCompose中的回调,并且thenCompose()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册Function函数时,Function函数将接收CompletableFuture实例的结果,并且该Function函数将返回1个CompletableFuture实例。这个Function函数返回的CompletableFuture的实例将决定thenCompose()注册时所返回的CompletaleFuture实例的状态。
// 在thenCompose注册时,会返回1个CompletableFuture实例,当Function回调正常执行完毕时,thenCompose注册返回的CompletableFuture实例的状态就取决于Function回调返回的CompletableFuture实例的状态。当Function回调执行过程中发生异常时,thenCompose返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;

// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenCompose中的回调。如果 completableFuture发生异常, 则不会执行thenCompose中的回调,并且thenCompose()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册Function函数时,Function函数将接收CompletableFuture实例的结果,并且该Function函数将返回1个CompletableFuture实例。这个Function函数返回的CompletableFuture的实例将决定thenCompose()注册时所返回的CompletaleFuture实例的状态。
// 在thenCompose注册时,会返回1个CompletableFuture实例,当Function回调正常执行完毕时,thenCompose注册返回的CompletableFuture实例的状态就取决于Function回调返回的CompletableFuture实例的状态。当Function回调执行过程中发生异常时,thenCompose返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) ;
java 复制代码
@Slf4j
public class TestCompletableFuture01 {

    public static void main(String[] args) {
        testThenCompose01();
    }

    private static void testThenCompose01() {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            log.info("runAsync start...");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // System.out.println(1/0);
            log.info("runAsync end...");
            return 1;
        });

        // sleep(3);

        CompletableFuture<String> retFuture = future.thenCompose((Integer i) -> {
            log.info("thenCompose start...");
            // System.out.println(1/0);
            CompletableFuture<String> composedFuture = CompletableFuture.supplyAsync(() -> {
                log.info("runAsync start2...");
                int j = i;
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(1/0);
                log.info("runAsync end2...");
                return String.valueOf(j + 1);
            });
            composedFuture.whenComplete((result, throwable) -> {
                log.info("composedFuture completed: " + result + throwable);
            });
            log.info("thenCompose end: " + composedFuture);
            return composedFuture;
        });

        retFuture.whenComplete((result, throwable) -> {
            log.info("retFuture completed: " + result + throwable);
        });

        log.info("retFuture: " + retFuture);

        sleep(60);

    }

    static void sleep(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}

handle

java 复制代码
// 给CompletableFuture实例注册1个结果转换的回调;
// 只要 completableFuture执行完(无论是正常完成还是异常完成), 则执行完时会执行handle中的回调。
// 如果注册时已执行完成(无论是正常完成还是异常完成),则由注册线程直接执行回调。如果注册时未执行完,则在完成时(无论是正常完成还是异常完成)由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiFunction回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiFunction回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiFunction回调;
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);

// 给CompletableFuture实例注册1个结果转换的回调;
// 只要 completableFuture执行完(无论是正常完成还是异常完成), 则执行完时会执行handle中的回调。
// 无论注册时该CompletableFuture实例是否已完成,都将在完成时(无论是正常完成还是异常完成)由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiFunction回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiFunction回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiFunction回调;
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);

// 给CompletableFuture实例注册1个结果转换的回调;
// 只要 completableFuture执行完(无论是正常完成还是异常完成), 则执行完时会执行handle中的回调。
// 无论注册时该CompletableFuture实例是否已完成,都将在完成时(无论是正常完成还是异常完成)由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiFunction回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiFunction回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiFunction回调;
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            sleep(2);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        sleep(3);

        CompletableFuture<Integer> handleFuture = future1.handle((v, t) -> {
            log.info("handle...start" + v + t);
            sleep(3);
            // 注意future1任务如果异常了,v是null
            int result = ((v==null) ? 0 : v) * 10;
            log.info("handle...end" + result);
            System.out.println(1 / 0);
            return result;
        });

        handleFuture.whenComplete((v, t) -> {
            log.info("handleFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }

}

2.6 结果消费

与结果处理和结果转换系列函数返回一个新的 CompletableFuture 不同,结果消费系列函数只对结果执行Action,而不返回新的计算值。

根据对结果的处理方式,结果消费函数又分为:

  • thenAccept系列:对单个结果进行消费
  • thenAcceptBoth系列:对两个结果进行消费
  • thenRun系列:不关心结果,只对结果执行Action
    thenAccept

通过观察该系列函数的参数类型可知,它们是函数式接口Consumer,这个接口只有输入,没有返回值。

java 复制代码
// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenAccept中的回调。如果 completableFuture发生异常, 则不会执行thenAccept中的回调,并且thenAccept()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时已正常执行完,则由注册线程直接执行回调。如果注册时未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Consumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Consumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Consumer回调;
public CompletionStage<Void> thenAccept(Consumer<? super T> action);

// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenAccept中的回调。如果 completableFuture发生异常, 则不会执行thenAccept中的回调,并且thenAccept()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Consumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Consumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Consumer回调;
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);

// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenAcceptAsync中的回调。如果 completableFuture发生异常, 则不会执行thenAcceptAsync中的回调,并且thenAcceptAsync()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Consumer回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当Consumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Consumer回调;
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture01 {

    public static void main(String[] args) {


        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.debug("future1 start...");
            sleep(2);
            int i = new Random().nextInt(10);
            log.debug("future1 end..." + i);
            // System.out.println(1 / 0);
            return i;
        });

        // 强制future1结束完了,再注册thenAccept(Async),这样就可以看出再注册thenAccept与thenAcceptAsync的区别了
        sleep(5);

        CompletableFuture<Void> future2 = future1.thenAccept(i -> {
            log.debug("future2 start...");
            sleep(2);
            i = i * 10;
            log.debug("future2 end..." + i);
            System.out.println(1 / 0);
        });

        future2.whenComplete((v, t) -> {
            log.debug("whenComplete..." + v + t);
        });

        log.info("main...ending");
        sleep(60);
    }

}

thenAcceptBoth

两个任务执行完成后,将结果交给thenAccepetBoth处理,无返回值

java 复制代码
// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行thenAccept中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行thenAcceptBoth中的回调,并且此时需要等CompletableFuture和other都完成后,thenAcceptBoth()注册返回CompletableFuture才是异常完成的状态;
// 如果注册时completableFuture 和 other 都已正常执行完,则由注册线程直接执行回调。如果注册时completableFuture 或 other 任一未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiConsumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiConsumer回调;
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);

// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行thenAccept中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行thenAcceptBoth中的回调,并且thenAcceptBoth()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiConsumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiConsumer回调;
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);

// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行thenAccept中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行thenAcceptBoth中的回调,并且thenAcceptBoth()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiConsumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiConsumer回调;
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action,     Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture01 {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.debug("future1 start...");
            sleep(2);
            // sleep(3);
            int i = new Random().nextInt(10);
            log.debug("future1 result:{}", i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.debug("future2 start...");
            sleep(3);
            // sleep(2);
            int i = new Random().nextInt(10);
            log.debug("future2 result:{}", i);
            // System.out.println(1 / 0);
            return i;
        });

        sleep(5);

        CompletableFuture<Void> thenAcceptBothFuture = future1.thenAcceptBoth(
                future2,
                (r1, r2) -> {
                    log.info("consume...r1:{}, r2:{}", r1, r2);
                    // System.out.println(1 / 0);
                    log.debug("sum:{} + {} = {}", r1, r2, r1 + r2);
                }
        );

        thenAcceptBothFuture.whenComplete((v, t) -> {
            log.debug("whenComplete..." + v + t);
        });

        log.info("main...ending");
        sleep(60);

    }

}

thenRun

thenRun 也是对线程任务结果的一种消费函数,与thenAccept不同的是,thenRun 会在上一阶段 CompletableFuture 计算完成的时候执行一个Runnable,Runnable并不使用该 CompletableFuture 计算的结果。

java 复制代码
// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenRun中的回调。如果 completableFuture发生异常, 则不会执行thenRun中的回调,并且thenRun()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时已正常执行完,则由注册线程直接执行回调。如果注册时未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个thenRun回调;
public CompletionStage<Void> thenRun(Runnable action);

// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenRun中的回调。如果 completableFuture发生异常, 则不会执行thenRun中的回调,并且thenRun()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个thenRun回调;
public CompletionStage<Void> thenRunAsync(Runnable action);

// 给CompletableFuture实例注册1个结果消费的回调;
// 如果 completableFuture正常执行完, 则执行完时会执行thenRun中的回调。如果 completableFuture发生异常, 则不会执行thenRun中的回调,并且thenRun()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Consumer回调正常执行完毕时,返回的CompletableFuture实例就是完成状态,当thenRun回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个thenRun回调;
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture01 {

    public static void main(String[] args) {

        testThenRun01();

    }

    private static void testThenRun01() {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync...start");
            sleep(2);
            int i = new Random().nextInt(10);
            log.info("supplyAsync...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        // sleep(5);

        CompletableFuture<Void> thenRunFuture = future.thenRun(() -> {
            log.info("thenRun...start");
            sleep(3);
            log.info("thenRun...end");
            // System.out.println(1 / 0);
        });

        thenRunFuture.whenComplete((result, throwable) -> {
            log.info("thenRunFuture completed: " + result + throwable);
        });

        log.info("main ends");

        sleep(60);

    }

}

2.7 结果组合

thenCombine

合并任务,有返回值

java 复制代码
// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行thenCombine中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行thenCombine中的回调,并且此时需要等CompletableFuture和other都完成后,thenCombine()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时completableFuture 和 other 都已正常执行完,则由注册线程直接执行回调。如果注册时completableFuture 或 other 任一未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiFunction回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiFunction回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiFunction回调;
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);

// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行thenCombine中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行thenCombine中的回调,并且此时需要等CompletableFuture和other都完成后,thenCombine()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiFunction回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiFunction回调;
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);

// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行thenCombine中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行thenCombine中的回调,,并且此时需要等CompletableFuture和other都完成后,thenCombine()注册返回CompletableFuture就是异常完成的状态;
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当BiFunction回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当BiConsumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个BiFunction回调;
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture01 {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            sleep(2);
            int i = new Random().nextInt(10);
            log.info("future1...end, result:{}", i);
            System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            sleep(5);
            int i = new Random().nextInt(10);
            log.info("future2...end, result:{}", i);
            // System.out.println(1 / 0);
            return i;
        });

        sleep(3);

        CompletableFuture<Integer> thenCombineFuture = future1.thenCombine(future2, (r1, r2) -> {
            int sum = r1 + r2;
            log.debug("thenCombine, sum:{} + {} = {}", r1, r2, sum);
            // System.out.println(1 / 0);
            return sum;
        });

        thenCombineFuture.whenComplete((v, t) -> {
            log.debug("thenCombineFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }

}

2.8 任务交互

所谓线程交互,是指将两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理。

applyToEither

两个任务哪个执行的快,就使用哪一个结果,有返回值

java 复制代码
// 给CompletableFuture实例注册1个 结果转换 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行applyToEither中的回调,并且在完成线程中执行,如果是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行applyToEither中的回调,并且在注册线程中执行,如果是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行applyToEither中的回调,并且在注册线程中执行,如果future1是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 在注册时,又会返回1个CompletableFuture实例,当Function回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);

// 给CompletableFuture实例注册1个 结果转换 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行applyToEither中的回调,如果是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行applyToEither中的回调,如果是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行applyToEither中的回调,如果future1是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);

// 给CompletableFuture实例注册1个 结果转换 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行applyToEither中的回调,如果是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行applyToEither中的回调,如果是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行applyToEither中的回调,如果future1是异常完成,则不会执行applyToEither中的回调,并且applyToEither返回的CompletableFuture实例就是异常完成状态。
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Function回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Function回调;
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            // sleep(2);
            // sleep(3);
            sleep(5);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            sleep(2);
            // sleep(3);
            // sleep(5);
            int i = new Random().nextInt(10);
            log.info("future2...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        // sleep(5);
        sleep(3);

        CompletableFuture<Integer> applyToEitherFuture = future1.applyToEither(future2, r -> {
            int applyToEitherResult = r * 10;
            log.info("applyToEither, result:{}, applyToEitherResult:{}", r, applyToEitherResult);
            // System.out.println(1 / 0);
            return applyToEitherResult;
        });

        applyToEitherFuture.whenComplete((v, t) -> {
            log.info("applyToEitherFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }

}

acceptEither

两个任务哪个执行的快,就消费哪一个结果,无返回值

java 复制代码
// 给CompletableFuture实例注册1个 结果消费 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行acceptEither中的回调,并且在完成线程中执行,如果是异常完成,则不会执行acceptEither中的回调,并且acceptEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行acceptEither中的回调,并且在注册线程中执行,如果是异常完成,则不会执行acceptEither中的回调,并且acceptEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行acceptEither中的回调,并且在注册线程中执行,如果future1是异常完成,则不会执行acceptEither中的回调,并且acceptEither返回的CompletableFuture实例就是异常完成状态。
// 在注册时,又会返回1个CompletableFuture实例,当Consumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Consumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Consumer回调;
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);

// 给CompletableFuture实例注册1个 结果转换 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行acceptEitherAsync中的回调,如果是异常完成,则不会执行acceptEitherAsync中的回调,并且acceptEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行acceptEitherAsync中的回调,如果是异常完成,则不会执行acceptEitherAsync中的回调,并且acceptEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行acceptEitherAsync中的回调,如果future1是异常完成,则不会执行acceptEitherAsync中的回调,并且acceptEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Consumer回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Consumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Consumer回调;
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);

// 给CompletableFuture实例注册1个 结果转换 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行acceptEitherAsync中的回调,如果是异常完成,则不会执行acceptEitherAsync中的回调,并且acceptEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行acceptEitherAsync中的回调,如果是异常完成,则不会执行acceptEitherAsync中的回调,并且acceptEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行acceptEitherAsync中的回调,如果future1是异常完成,则不会执行acceptEitherAsync中的回调,并且acceptEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Function回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Consumer回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Consumer回调;
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            // sleep(2);
            sleep(3);
            // sleep(5);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            sleep(2);
            // sleep(3);
            // sleep(5);
            int i = new Random().nextInt(10);
            log.info("future2...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        sleep(5);
        // sleep(3);

        CompletableFuture<Void> acceptEitherFuture = future1.acceptEither(future2, r -> {
            int acceptEitherResult = r * 10;
            log.info("acceptEither, result:{}, acceptEitherResult:{}", r, acceptEitherResult);
            // System.out.println(1 / 0);
        });

        acceptEitherFuture.whenComplete((v, t) -> {
            log.info("applyToEitherFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }

}

runAfterEither

任意一个任务执行完成,进行下一步操作(Runnable类型任务)

java 复制代码
// 给CompletableFuture实例注册1个 运行完成后 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行runAfterEither中的回调,并且在完成线程中执行,如果是异常完成,则不会执行runAfterEither中的回调,并且runAfterEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行runAfterEither中的回调,并且在注册线程中执行,如果是异常完成,则不会执行runAfterEither中的回调,并且runAfterEither返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行runAfterEither中的回调,并且在注册线程中执行,如果future1是异常完成,则不会执行runAfterEither中的回调,并且runAfterEither返回的CompletableFuture实例就是异常完成状态。
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Runnable回调;
public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);

// 给CompletableFuture实例注册1个 运行完成后 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行runAfterEitherAsync中的回调,如果是异常完成,则不会执行runAfterEitherAsync中的回调,并且runAfterEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行runAfterEitherAsync中的回调,如果是异常完成,则不会执行runAfterEitherAsync中的回调,并且runAfterEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行runAfterEitherAsync中的回调,如果future1是异常完成,则不会执行runAfterEitherAsync中的回调,并且runAfterEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由异步线程池(forkJoinPool,默认都是守护线程)执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Runnable回调;
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);

// 给CompletableFuture实例注册1个 运行完成后 的回调;
// 如果注册时,completableFuture 和 other 都未完成,后续第1个完成的(无论是正常完成还是异常完成),如果是正常完成,则正常完成时会执行runAfterEitherAsync中的回调,如果是异常完成,则不会执行runAfterEitherAsync中的回调,并且runAfterEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中只有1个完成,则如果这个是正常完成,则正常完成时会执行runAfterEitherAsync中的回调,如果是异常完成,则不会执行runAfterEitherAsync中的回调,并且runAfterEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 如果注册时,completableFuture 和 other 中2个都完成了,则如果future1是正常完成,则会执行runAfterEitherAsync中的回调,如果future1是异常完成,则不会执行runAfterEitherAsync中的回调,并且runAfterEitherAsync返回的CompletableFuture实例就是异常完成状态。
// 无论注册时该CompletableFuture实例是否已完成,都将在正常完成时由指定的线程池执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Runnable回调;
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            // sleep(2);
            sleep(3);
            // sleep(5);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            sleep(2);
            // sleep(3);
            // sleep(5);
            int i = new Random().nextInt(10);
            log.info("future2...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        // sleep(5);
        // sleep(3);

        CompletableFuture<Void> runAfterEitherFuture = future1.runAfterEither(future2, () -> {
            log.info("runAfterEither executed...");
            // System.out.println(1 / 0);
        });

        runAfterEitherFuture.whenComplete((v, t) -> {
            log.info("runAfterEitherFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }

}

runAfterBoth

两个任务都执行完成后,执行下一步操作(Runnable类型任务)

java 复制代码
// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行runAfterBoth中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行runAfterBoth中的回调,并且此时需要等CompletableFuture和other都完成后,runAfterBoth()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时completableFuture 和 other 都已正常执行完,则由注册线程直接执行回调。如果注册时completableFuture 或 other 任一未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Runnable回调;
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);

// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行runAfterBothAsync中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行runAfterBothAsync中的回调,并且此时需要等CompletableFuture和other都完成后,runAfterBothAsync()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时completableFuture 和 other 都已正常执行完,则由注册线程直接执行回调。如果注册时completableFuture 或 other 任一未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Runnable回调;
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);

// 给CompletableFuture实例注册1个结果组合的回调;
// 如果 completableFuture 和 other 都正常执行完, 则执行完时会执行runAfterBothAsync中的回调。如果 completableFuture 或 other 任一发生异常, 则不会执行runAfterBothAsync中的回调,并且此时需要等CompletableFuture和other都完成后,runAfterBothAsync()注册返回CompletableFuture就是异常完成的状态;
// 如果注册时completableFuture 和 other 都已正常执行完,则由注册线程直接执行回调。如果注册时completableFuture 或 other 任一未执行完,则在正常完成时由完成线程执行回调;
// 在注册时,又会返回1个CompletableFuture实例,当Runnable回调执行正常完毕时,返回的CompletableFuture实例就是完成状态,当Runnable回调执行发生异常时,返回的CompletableFuture就是异常完成的状态;
// 同1个CompletableFuture实例,可以调用这个方法多次,从而注册多个Runnable回调;
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor);
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            // sleep(2);
            sleep(3);
            // sleep(5);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            // sleep(2);
            // sleep(3);
            sleep(5);
            int i = new Random().nextInt(10);
            log.info("future2...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        // sleep(5);
        // sleep(3);

        CompletableFuture<Void> runAfterBothFuture = future1.runAfterBoth(future2, () -> {
            log.info("runAfterBoth executed...");
            // System.out.println(1 / 0);
        });

        runAfterBothFuture.whenComplete((v, t) -> {
            log.info("runAfterBothFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }

}

anyOf

anyOf 方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个 CompletableFuture。

java 复制代码
// 创建1个CompletableFuture,并关联一批CompletableFuture实例;
// 当这一批CompletableFuture实例有任意一个完成时,返回的CompletableFuture实例处于完成;
// 当这一批CompletableFuture实例中先完成的是正常完成时,该CompletableFuture就处于正常完成状态,当这一批CompletableFuture先完成的异常完成,该CompletableFuture就处于异常完成;
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            sleep(3);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            sleep(1);
            int i = new Random().nextInt(10);
            log.info("future2...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            log.info("future3...start");
            sleep(2);
            int i = new Random().nextInt(10);
            log.info("future3...end" + i);
            System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);

        anyOfFuture.whenComplete((v, t) -> {
            log.info("anyOfFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);

    }
}

allOf

allOf方法用来实现多 CompletableFuture 的同时返回。

java 复制代码
// 创建1个CompletableFuture,并关联一批CompletableFuture实例;
// 当这一批CompletableFuture实例全部完成时,返回的CompletableFuture实例处于完成;
// 当这一批CompletableFuture实例全部都正常完成时,该CompletableFuture就处于正常完成状态,当这一批CompletableFuture有任何一个异常完成,该CompletableFuture就处于异常完成;
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
java 复制代码
@Slf4j
public class TestCompletableFuture {

    public static void main(String[] args) {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            log.info("future1...start");
            sleep(3);
            int i = new Random().nextInt(10);
            log.info("future1...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            log.info("future2...start");
            sleep(1);
            int i = new Random().nextInt(10);
            log.info("future2...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            log.info("future3...start");
            sleep(2);
            int i = new Random().nextInt(10);
            log.info("future3...end" + i);
            // System.out.println(1 / 0);
            return i;
        });

        CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2, future3);

        allOfFuture.whenComplete((v, t) -> {
            log.info("allOfFuture whenComplete..." + v + t);
        });

        log.info("main...ending");

        sleep(60);
    }
}

图示总结

CompletableFuture创建方式总结

一元依赖使用方式总结

这些方法的主要区别在于它们对异步性的处理(同步或异步)以及是否允许使用自定义的线程池。

使用这些方法时,可以根据具体需求选择最合适的操作,以实现对异步计算结果的灵活处理。

二元依赖使用方式总结

CompletableFuture 的 AND 组合关系方法(thenCombine, thenCombineAsync, thenAcceptBoth, thenAcceptBothAsync, runAfterBoth, runAfterBothAsync)在两个 CompletableFuture 都成功完成后执行某个操作,而 OR 组合关系方法(applyToEither, applyToEitherAsync, acceptEither, acceptEitherAsync, runAfterEither, runAfterEitherAsync)在两个 CompletableFuture 中任意一个完成时执行操作,其中 AND 组合关系方法关注结果的组合和消费,OR 组合关系方法关注对第一个完成的结果的快速反应。

2.9 其它方法

completedFuture(U value)

java 复制代码
// 立即创建一个已经完成的CompletableFuture对象。
// 这个方法通常用于创建一个已经包含结果的CompletableFuture,而不需要等待异步任务完成。
public static <U> CompletableFuture<U> completedFuture(U value);

public T join();

java 复制代码
public T join();

getNow(T valueIfAbsent)

java 复制代码
public T getNow(T valueIfAbsent);

complete(T value)

java 复制代码
//(注意,通过提交异步任务之后返回的CompletableFuture的实例,默认等到异步任务执行完成后(不管是正常完成,还是异常完成),会变为完成状态。
//       也可以直接调用complete(T value) 让CompletableFuture的实例提前正常完成 或 completeExceptionally(Throwable ex) 让CompletableFuture的实例提前异常完成。
//       未关联Runnable或Supplier的new CompletableFuture()也可以手动让CompletableFuture完成的成员方法: complete(T value); completeExceptionally(Throwable ex)。
// )
public boolean complete(T value);

completeExceptionally(Throwable ex)

java 复制代码
// 手动将 CompletableFuture 设置为异常完成状态,并传入一个 RuntimeException 对象。
// 这样,isCompletedExceptionally() 也会返回 true
public boolean completeExceptionally(Throwable ex);

isCompletedExceptionally()

java 复制代码
// 用于检查 CompletableFuture 是否由于异常而完成。如果 CompletableFuture 由于异常(包括取消异常)而完成,返回 true。
public boolean isCompletedExceptionally();

obtrudeValue(T value)

java 复制代码
// obtrudeValue 方法用于将 CompletableFuture 的结果强制设置为提供的值。
// 如果 CompletableFuture 已经以任何方式完成(无论是正常完成、异常完成还是被取消),那么调用 obtrudeValue 将不会有任何效果。
public void obtrudeValue(T value);

obtrudeException(Throwable ex)

java 复制代码
// obtrudeException 方法用于将 CompletableFuture 的结果强制设置为由提供的异常引起的异常完成。
// 如果 CompletableFuture 已经完成,调用 obtrudeException 将不会有任何效果。


public void obtrudeException(Throwable ex);

getNumberOfDependents()

java 复制代码
// CompletableFuture 的 getNumberOfDependents 方法返回一个整数,表示有多少异步操作或者依赖是注册在当前 CompletableFuture 实例上的。
// 这个方法可以用来了解和调试 CompletableFuture 对象上的链式调用情况。
public int getNumberOfDependents();

3.0 使用案例:实现最优的"烧水泡茶"程序

著名数学家华罗庚先生在《统筹方法》这篇文章里介绍了一个烧水泡茶的例子,文中提到最优的工序应该是下面这样:

对于烧水泡茶这个程序,一种最优的分工方案:用两个线程 T1 和 T2 来完成烧水泡茶程序,T1 负责洗水壶、烧开水、泡茶这三道工序,T2 负责洗茶壶、洗茶杯、拿茶叶三道工序,其中 T1 在执行泡茶这道工序时需要等待 T2 完成拿茶叶的工序。

基于Future实现

java 复制代码
public class FutureTaskDemo3{

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        // 创建任务T2的FutureTask
        FutureTask<String> ft2 = new FutureTask<>(new T2Task());
        
        // 创建任务T1的FutureTask
        FutureTask<String> ft1 = new FutureTask<>(new T1Task(ft2));

        // 线程T1执行任务ft1
        Thread T1 = new Thread(ft1);
        T1.start();
        
        // 线程T2执行任务ft2
        Thread T2 = new Thread(ft2);
        T2.start();
        
        // 等待线程T1执行结果
        System.out.println(ft1.get());

    }
}

// T1Task需要执行的任务:
// 洗水壶、烧开水、泡茶
class T1Task implements Callable<String> {
    FutureTask<String> ft2;
    // T1任务需要T2任务的FutureTask
    T1Task(FutureTask<String> ft2){
        this.ft2 = ft2;
    }
    @Override
    public String call() throws Exception {
        
        System.out.println("T1:洗水壶...");
        TimeUnit.SECONDS.sleep(1);

        System.out.println("T1:烧开水...");
        TimeUnit.SECONDS.sleep(15);
        
        // 获取T2线程的茶叶
        String tf = ft2.get();
        System.out.println("T1:拿到茶叶:"+tf);

        System.out.println("T1:泡茶...");
        
        return "上茶:" + tf;
    }
}

// T2Task需要执行的任务:
// 洗茶壶、洗茶杯、拿茶叶
class T2Task implements Callable<String> {
    
    @Override
    public String call() throws Exception {
        
        System.out.println("T2:洗茶壶...");
        TimeUnit.SECONDS.sleep(1);

        System.out.println("T2:洗茶杯...");
        TimeUnit.SECONDS.sleep(2);

        System.out.println("T2:拿茶叶...");
        TimeUnit.SECONDS.sleep(1);
        
        return "龙井";
    }
}

基于CompletableFuture实现

java 复制代码
public class CompletableFutureDemo2 {

    public static void main(String[] args) {

        //任务1:洗水壶->烧开水
        CompletableFuture<Void> f1 = CompletableFuture
                .runAsync(() -> {
                    System.out.println("T1:洗水壶...");
                    sleep(1, TimeUnit.SECONDS);

                    System.out.println("T1:烧开水...");
                    sleep(15, TimeUnit.SECONDS);
                });
                
        //任务2:洗茶壶->洗茶杯->拿茶叶
        CompletableFuture<String> f2 = CompletableFuture
                .supplyAsync(() -> {
                    
                    System.out.println("T2:洗茶壶...");
                    sleep(1, TimeUnit.SECONDS);

                    System.out.println("T2:洗茶杯...");
                    sleep(2, TimeUnit.SECONDS);

                    System.out.println("T2:拿茶叶...");
                    sleep(1, TimeUnit.SECONDS);
                    
                    return "龙井";
                });
                
        //任务3:任务1和任务2完成后执行:泡茶
        CompletableFuture<String> f3 = f1.thenCombine(f2, (boiledWater, teaLeaf) -> {
                    System.out.println("T1:拿到茶叶:" + teaLeaf);
                    System.out.println("T1:泡茶...");
                    return "上茶:" + tf;
                });
                
        //等待任务3执行结果
        System.out.println(f3.join());
    }

    static void sleep(int t, TimeUnit u){
        try {
            u.sleep(t);
        } catch (InterruptedException e) {
        }
    }
}
相关推荐
青云计划6 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿6 小时前
Jsoniter(java版本)使用介绍
java·开发语言
探路者继续奋斗7 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
消失的旧时光-19437 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
A懿轩A7 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
乐观勇敢坚强的老彭8 小时前
c++寒假营day03
java·开发语言·c++
biubiubiu07068 小时前
谷歌浏览器无法访问localhost:8080
java
大黄说说8 小时前
新手选语言不再纠结:Java、Python、Go、JavaScript 四大热门语言全景对比与学习路线建议
java·python·golang
烟沙九洲8 小时前
Java 中的 封装、继承、多态
java
识君啊9 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端