CompletableFuture使用方法的详细说明
异步执行一个任务时,我们一般是使用自定义的线程池Executor去创建执行的。如果不需要有返回值, 任务实现Runnable接口;如果需要有返回值,任务实现Callable接口,调用Executor的submit方法,再使用Future获取即可。
如果多个线程存在前后依赖的话,我们怎么处理呢?可使用同步组件CountDownLatch、CyclicBarrier等,但是比较麻烦。
其实还有比较简单的方法, 那就是使用CompeletableFuture。

本文涉及知识点,参考了这里
1.回顾Future
因为CompletableFuture实现了Future接口,我们先来回顾Future吧。
Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。
如果主线程需要执行一个很耗时的计算任务,我们就可以通过future把这个任务放到异步线程中执行。
主线程继续处理其他任务,处理完成后,再通过Future获取计算结果。
来看个简单例子吧,假设我们有两个任务服务(模拟实际业务),一个获取当前时间,一个是获取用户名。如下:
/**
 * @author jiangkd
 * @date 2022/8/25 16:37:45
 */
@Service
public class DateService {
    /**
     * 模拟接口调用
     *
     * @return value
     */
    public String getDate() {
        // 模拟处理
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "现在的时间是" + DateUtil.now();
    }
}
/**
 * @author jiangkd
 * @date 2022/8/25 16:37:57
 */
@Service
public class UserService {
    /**
     * 模拟接口调用
     *
     * @return value
     */
    public String getName() {
        // 模拟处理
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Tom";
    }
}
接下来,我们来演示下,在主线程中是如何使用Future来进行异步调用的。
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class FutureTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    @Resource
    UserService userService;
    /**
     * 使用Future来进行异步调用的
     */
    @Test
    public void test() throws InterruptedException, ExecutionException, TimeoutException {
        //
        final StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // getDate
        final Future<String> getDateFutureTask = threadPoolTaskExecutor.submit(() -> dateService.getDate());
        //模拟主线程其它操作耗时
        Thread.sleep(2000);
        // getName
        final Future<String> getNameFutureTask = threadPoolTaskExecutor.submit(() -> userService.getName());
        // 获取两个线程执行的结果
        final String getDate = getDateFutureTask.get(2, TimeUnit.SECONDS);
        final String getName = getNameFutureTask.get();
        stopWatch.stop();
        log.info("date:{}", getDate);
        log.info("name:{}", getName);
        //
        log.info(stopWatch.prettyPrint());
    }
}
运行结果:
2022-08-25 16:50:36.968  INFO 21692 --- [           main] example.demo.jiangkd.thread.FutureTest   : date:现在的时间是2022-08-25 16:50:36
2022-08-25 16:50:36.970  INFO 21692 --- [           main] example.demo.jiangkd.thread.FutureTest   : name:Tom
2022-08-25 16:50:36.970  INFO 21692 --- [           main] example.demo.jiangkd.thread.FutureTest   : StopWatch '': running time (millis) = 3048
-----------------------------------------
ms     %     Task name
-----------------------------------------
03048  100%
如果我们不使用Future进行并行异步调用,而是在主线程串行进行的话,耗时大约为3000+2000+1000 = 6000ms左右。可以发现,future+线程池异步配合,提高了程序的执行效率。
注意: 代码中的 threadPoolTaskExecutor 是我自己定义的线程池。 详情请参考 ThreadPoolTaskExecutor线程池创建
这里有个注意点, 代码中 getDataFutureTask.get(2, TimeUnit.SECONDS) 并没有报出TimeoutException, getDate方法中睡眠了3s, 这里get的时候, 如果超过2s线程没有结束, 是要报错的, 那么为什么没有报错呢? 因为主线程中我们 Thread.sleep(2000) 睡眠了2s, 此时getDataFutureTask的线程已经开始执行了, 睡眠2s后才进行get阻塞, 此时只等待了大概1s左右的时间线程就执行完了, 所以没有报错, 如果把 Thread.sleep(2000) 注释掉, 就会报错了, 可以自行试一试。
但是Future对于结果的获取,不是很友好,只能通过阻塞或者轮询的方式得到任务的结果。
- Future.get() 就是阻塞调用,在线程获取结果之前get方法会一直阻塞。
- Future提供了一个isDone方法,可以在程序中轮询这个方法查询执行结果。
阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的CPU资源。因此,JDK8设计出CompletableFuture。CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。
2.走进CompletableFuture
我们还是基于以上Future的例子,改用CompletableFuture 来实现
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class CompletableFutureTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    @Resource
    UserService userService;
    /**
     * 基于FutureTest中的方法test的例子,改用CompletableFuture 来实现
     *
     * @throws InterruptedException e
     * @throws ExecutionException   e
     * @throws TimeoutException     e
     */
    @Test
    public void test() throws InterruptedException, ExecutionException, TimeoutException {
        //
        final StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // getDate
        final CompletableFuture<String> getDateCompletableFuture =
                CompletableFuture.supplyAsync(() -> dateService.getDate(), threadPoolTaskExecutor);
        //模拟主线程其它操作耗时
        Thread.sleep(2000);
        // getName
        final CompletableFuture<String> getNameCompletableFuture =
                CompletableFuture.supplyAsync(() -> userService.getName(), threadPoolTaskExecutor);
        // 获取两个线程执行的结果
        final String getDate = getDateCompletableFuture.get(2, TimeUnit.SECONDS);
        final String getName = getNameCompletableFuture.get();
        stopWatch.stop();
        log.info("date:{}", getDate);
        log.info("name:{}", getName);
        //
        log.info(stopWatch.prettyPrint());
    }
}
可以发现,使用CompletableFuture,并没有让代码简洁多少,这里都是用了lambda表达式进行简化,更多CompletableFuture的使用继续往下看就行。
CompletableFuture的supplyAsync方法,提供了异步执行的功能,如果第二个参数不传的话,线程池也不用单独创建了。实际上,CompletableFuture使用了默认线程池是ForkJoinPool.commonPool。
CompletableFuture提供了几十种方法,辅助我们的异步任务场景。这些方法包括创建异步任务、任务异步回调、多个任务组合处理等方面。我们一起来学习吧
3.CompletableFuture的使用场景

3.1.创建异步任务
CompletableFuture创建异步任务,一般有supplyAsync和runAsync两个方法

- supplyAsync: 执行CompletableFuture任务,支持返回值。
- runAsync: 执行CompletableFuture任务,没有返回值。
3.1.1.supplyAsync方法
// 使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
// 自定义线程,根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class SupplyAsyncTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    @Resource
    UserService userService;
    /**
     * 自定义线程,根据runnable构建执行任务
     */
    @Test
    public void test() throws ExecutionException, InterruptedException {
        // 使用自定义线程池, 线程中打印线程名称
        final CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            log.info("执行线程, 线程名称:{}", Thread.currentThread().getName());
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        // 使用默认的线程池, 线程中打印线程名称
        final CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            log.info("执行线程, 线程名称:{}", Thread.currentThread().getName());
            return userService.getName();
        });
        // 分别获取两个线程的返回值
        final String date = completableFuture1.get();
        log.info("getDate:{}", date);
        final String name = completableFuture2.get();
        log.info("getName:{}", name);
    }
}
运行结果
2022-08-27 08:54:06.444  INFO 5968 --- [-example-task-1] e.d.j.t.c.SupplyAsyncTest                : 执行线程, 线程名称:demo-example-task-1
2022-08-27 08:54:06.445  INFO 5968 --- [onPool-worker-9] e.d.j.t.c.SupplyAsyncTest                : 执行线程, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 08:54:09.506  INFO 5968 --- [           main] e.d.j.t.c.SupplyAsyncTest                : getDate:现在的时间是2022-08-27 08:54:09
2022-08-27 08:54:09.507  INFO 5968 --- [           main] e.d.j.t.c.SupplyAsyncTest                : getName:Tom
通过运行结果发现, 两个线程执行打印的线程名称是不同的, 第一个getDate的线程使用的是我们自己定义的线程池, 自定义的线程池的线程前缀是demo-example-task-, 而第二个getName的线程使用的就是默认的内置线程池ForkJoinPool.commonPool()。
再说一遍, 自定义的线程池参考 ThreadPoolTaskExecutor线程池创建。
运行结果中也打印出了两个线程的返回结果。
3.1.2.runAsync方法
// 使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable)
// 自定义线程,根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable,  Executor executor)

从上图看出, 如果此时使用了runAsync方法, 线程中不可以用return返回值, 否则会报错。
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class RunAsyncTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    @Resource
    UserService userService;
    @Test
    public void test() throws ExecutionException, InterruptedException {
        // 使用自定义线程池, 线程中打印线程名称
        final CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(() -> {
            log.info("执行线程, 线程名称:{}", Thread.currentThread().getName());
            dateService.getDate();
        }, threadPoolTaskExecutor);
        // 使用默认的线程池, 线程中打印线程名称
        final CompletableFuture<Void> completableFuture2 = CompletableFuture.runAsync(() -> {
            log.info("执行线程, 线程名称:{}", Thread.currentThread().getName());
            userService.getName();
        });
        // 分别获取两个线程的返回值
        final Void unused1 = completableFuture1.get();
        log.info("getDate:{}", unused1);
        final Void unused2 = completableFuture2.get();
        log.info("getName:{}", unused2);
    }
}
执行结果:
2022-08-27 09:06:06.527  INFO 30060 --- [onPool-worker-9] e.d.j.t.completablefuture.RunAsyncTest   : 执行线程, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 09:06:06.527  INFO 30060 --- [-example-task-1] e.d.j.t.completablefuture.RunAsyncTest   : 执行线程, 线程名称:demo-example-task-1
2022-08-27 09:06:09.588  INFO 30060 --- [           main] e.d.j.t.completablefuture.RunAsyncTest   : getDate:null
2022-08-27 09:06:09.588  INFO 30060 --- [           main] e.d.j.t.completablefuture.RunAsyncTest   : getName:null
示例代码中我们将supplyAsync改为了runAsync方法,线程中也没有使用return(否则会报错,,如面上的截图),最后通过get方法获取两个线程的执行结果也都是null。
和supplyAsync的示例一样, getDate线程依然使用了自定义的线程池,getName使用了默认内置的, 通过执行结果可知。
细心的你有没有发现,runAsync示例中,CompletableFuture的泛型是Void,所以get方法得到的返回就是Void,Void是什么呢? java.lang.Void是void 关键字的包装类。Void类是一个不可实例化的占位符类,如果方法返回值是Void类型,那么该方法只能返回null类型。
3.2.任务异步回调

3.2.1.thenRun/thenRunAsync
CompletableFuture的thenRun/thenRunAsync方法,通俗点讲就是,做完第一个任务后,再做第二个任务。也就是说某个任务执行完成后,执行回调方法;但是前后两个任务没有参数传递,第二个任务也没有返回值。
public CompletableFuture<Void> thenRun(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action);
thenRun 和thenRunAsync有什么区别呢? 可以看下源码:
   private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
    public CompletableFuture<Void> thenRun(Runnable action) {
        return uniRunStage(null, action);
    }
    public CompletableFuture<Void> thenRunAsync(Runnable action) {
        return uniRunStage(asyncPool, action);
    }
如果执行第一个任务的时候,没有传入自定义的线程池,那么thenRun/thenRunAsync理所当然的都是用默认内置的ForkJoin线程池,可能都是用的同一个线程。
相反如果你执行第一个任务的时候,传入了一个自定义线程池:
- 调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池,所以可能用到的是同一个线程。
- 调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池。
thenRun示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class ThenRunAndThenRunAsyncTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    /**
     * thenRun方法测试, 链式调用, 执行supplyAsync后接着执行thenRun
     */
    @Test
    public void thenRunTest() throws ExecutionException, InterruptedException {
        /*
        1.thenRun, 使用自定义线程池
         */
        final CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 使用了supplyAsync,返回值
            return dateService.getDate();
        }, threadPoolTaskExecutor).thenRun(() -> {
            //
            log.info("thenRun线程执行, 线程名称:{}", Thread.currentThread().getName());
        });
        final Void unused1 = completableFuture1.get();
        log.info("completableFuture1返回结果:{}", unused1);
        log.info("=====================================================");
        /*
        2.thenRun, 不使用自定义线程池
         */
        final CompletableFuture<Void> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 使用了supplyAsync,返回值
            return dateService.getDate();
        }).thenRun(() -> {
            //
            log.info("thenRun线程执行, 线程名称:{}", Thread.currentThread().getName());
        });
        final Void unused2 = completableFuture2.get();
        log.info("completableFuture1返回结果:{}", unused2);
    }
}
运行结果:
2022-08-27 09:42:39.668  INFO 35820 --- [-example-task-1] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 09:42:42.725  INFO 35820 --- [-example-task-1] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : thenRun线程执行, 线程名称:demo-example-task-1
2022-08-27 09:42:42.725  INFO 35820 --- [           main] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : completableFuture1返回结果:null
2022-08-27 09:42:42.725  INFO 35820 --- [           main] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : =====================================================
2022-08-27 09:42:42.726  INFO 35820 --- [onPool-worker-9] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : supplyAsync线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 09:42:45.738  INFO 35820 --- [onPool-worker-9] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : thenRun线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 09:42:45.738  INFO 35820 --- [           main] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : completableFuture1返回结果:null
thenRunAsync示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class ThenRunAndThenRunAsyncTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    /**
     * thenRunAsync方法测试, 链式调用, 执行supplyAsync后接着执行thenRunAsync
     */
    @Test
    public void thenRunAsyncTest() throws ExecutionException, InterruptedException {
        /*
        1.thenRunAsync, 使用自定义线程池
         */
        final CompletableFuture<Void> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 使用了supplyAsync,返回值
            return dateService.getDate();
        }, threadPoolTaskExecutor).thenRunAsync(() -> {
            //
            log.info("thenRunAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
        });
        final Void unused1 = completableFuture1.get();
        log.info("completableFuture1返回结果:{}", unused1);
        log.info("=====================================================");
        /*
        2.thenRunAsync, 不使用自定义线程池
         */
        final CompletableFuture<Void> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 使用了supplyAsync,返回值
            return dateService.getDate();
        }).thenRunAsync(() -> {
            //
            log.info("thenRunAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
        });
        final Void unused2 = completableFuture2.get();
        log.info("completableFuture1返回结果:{}", unused2);
    }
}
执行结果
2022-08-27 09:48:03.013  INFO 776 --- [-example-task-1] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 09:48:06.078  INFO 776 --- [onPool-worker-9] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : thenRunAsync线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 09:48:06.078  INFO 776 --- [           main] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : completableFuture1返回结果:null
2022-08-27 09:48:06.078  INFO 776 --- [           main] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : =====================================================
2022-08-27 09:48:06.078  INFO 776 --- [onPool-worker-9] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : supplyAsync线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 09:48:09.090  INFO 776 --- [onPool-worker-9] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : thenRunAsync线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 09:48:09.090  INFO 776 --- [           main] e.d.j.t.c.ThenRunAndThenRunAsyncTest     : completableFuture1返回结果:null
注意:后面介绍的thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也和thenRun和thenRunAsync相同。
3.2.2.thenAccept/thenAcceptAsync
CompletableFuture的thenAccept/thenAcceptAsync方法表示,第一个任务执行完成后,执行第二个回调方法任务,但是会将第一个任务的执行结果,作为入参,传递到回调方法中,但是回调方法是没有返回值的。
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class ThenAcceptAndThenAcceptAsyncTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    /**
     * thenAccept
     */
    @Test
    public void thenAcceptTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            final String date = dateService.getDate();
            log.info("supplyAsync线程执行, 返回值:{}", date);
            return date;
        }, threadPoolTaskExecutor).thenAccept((value) -> {
            //
            log.info("thenAccept线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("thenAccept线程执行, 获取supplyAsync线程的返回值:{}", value);
        });
        final Void unused = completableFuture.get();
        log.info("返回结果:{}", unused);
    }
    /**
     * thenAcceptAsync
     */
    @Test
    public void thenAcceptAsyncTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            final String date = dateService.getDate();
            log.info("supplyAsync线程执行, 返回值:{}", date);
            return date;
        }, threadPoolTaskExecutor).thenAcceptAsync((value) -> {
            //
            log.info("thenAcceptAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("thenAcceptAsync线程执行, 获取supplyAsync线程的返回值:{}", value);
        });
        final Void unused = completableFuture.get();
        log.info("返回结果:{}", unused);
    }
}
thenAcceptTest执行结果:
2022-08-27 10:05:40.741  INFO 37528 --- [-example-task-1] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:05:43.793  INFO 37528 --- [-example-task-1] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : supplyAsync线程执行, 返回值:现在的时间是2022-08-27 10:05:43
2022-08-27 10:05:43.803  INFO 37528 --- [-example-task-1] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : thenAccept线程执行, 线程名称:demo-example-task-1
2022-08-27 10:05:43.803  INFO 37528 --- [-example-task-1] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : thenAccept线程执行, 获取supplyAsync线程的返回值:现在的时间是2022-08-27 10:05:43
2022-08-27 10:05:43.803  INFO 37528 --- [           main] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : 返回结果:null
thenAcceptAsyncTest执行结果
2022-08-27 10:06:55.593  INFO 20144 --- [-example-task-1] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:06:58.641  INFO 20144 --- [-example-task-1] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : supplyAsync线程执行, 返回值:现在的时间是2022-08-27 10:06:58
2022-08-27 10:06:58.641  INFO 20144 --- [onPool-worker-9] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : thenAcceptAsync线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 10:06:58.641  INFO 20144 --- [onPool-worker-9] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : thenAcceptAsync线程执行, 获取supplyAsync线程的返回值:现在的时间是2022-08-27 10:06:58
2022-08-27 10:06:58.641  INFO 20144 --- [           main] d.j.t.c.ThenAcceptAndThenAcceptAsyncTest : 返回结果:null
3.2.3. thenApply/thenApplyAsync
CompletableFuture的thenApply/thenApplyAsync方法表示,第一个任务执行完成后,执行第二个回调方法任务,但是会将第一个任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class ThenApplyAndThenApplyAsyncTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    UserService userService;
    /**
     * thenApply
     */
    @Test
    public void thenApplyTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            final String name = userService.getName();
            log.info("supplyAsync线程执行, 返回值:{}", name);
            return name;
        }, threadPoolTaskExecutor).thenApply((value) -> {
            //
            log.info("thenApply线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("thenApply线程执行, 获取supplyAsync线程的返回值:{}", value);
            return "thenApply返回, value是supplyAsync的结果, " + value;
        });
        final String name = completableFuture.get();
        log.info("返回结果:{}", name);
    }
    /**
     * thenApplyAsync
     */
    @Test
    public void thenApplyAsyncTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            final String name = userService.getName();
            log.info("supplyAsync线程执行, 返回值:{}", name);
            return name;
        }, threadPoolTaskExecutor).thenApplyAsync((value) -> {
            //
            log.info("thenApplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("thenApplyAsync线程执行, 获取supplyAsync线程的返回值:{}", value);
            return "thenApplyAsync返回, value是supplyAsync的结果, " + value;
        });
        final String name = completableFuture.get();
        log.info("返回结果:{}", name);
    }
}
thenApplyTest的执行结果:
2022-08-27 10:14:16.608  INFO 38224 --- [-example-task-1] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:14:17.618  INFO 38224 --- [-example-task-1] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : supplyAsync线程执行, 返回值:Tom
2022-08-27 10:14:17.618  INFO 38224 --- [-example-task-1] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : thenApply线程执行, 线程名称:demo-example-task-1
2022-08-27 10:14:17.618  INFO 38224 --- [-example-task-1] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : thenApply线程执行, 获取supplyAsync线程的返回值:Tom
2022-08-27 10:14:17.619  INFO 38224 --- [           main] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : 返回结果:thenApply返回, value是supplyAsync的结果, Tom
thenApplyAsyncTest的执行结果:
2022-08-27 10:14:35.846  INFO 47200 --- [-example-task-1] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:14:36.859  INFO 47200 --- [-example-task-1] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : supplyAsync线程执行, 返回值:Tom
2022-08-27 10:14:36.860  INFO 47200 --- [onPool-worker-9] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : thenApplyAsync线程执行, 线程名称:ForkJoinPool.commonPool-worker-9
2022-08-27 10:14:36.861  INFO 47200 --- [onPool-worker-9] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : thenApplyAsync线程执行, 获取supplyAsync线程的返回值:Tom
2022-08-27 10:14:36.861  INFO 47200 --- [           main] e.d.j.t.c.ThenApplyAndThenApplyAsyncTest : 返回结果:thenApplyAsync返回, value是supplyAsync的结果, Tom
3.2.4.exceptionally
CompletableFuture的exceptionally方法表示,某个任务执行异常时,执行的回调处理方法,并且有抛出异常作为参数,传递到回调方法,回调方法有返回值。
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class ExceptionallyTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    UserService userService;
    /**
     * exceptionally
     */
    @Test
    public void exceptionallyTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> completedFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 异常触发
            int i = 1 / 0;
            return userService.getName();
        }, threadPoolTaskExecutor).exceptionally((e) -> {
            log.info("exceptionally线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.error("exceptionally线程执行, 得到supplyAsync中的异常堆栈信息", e);
            return "exceptionally得到supplyAsync的异常信息, " + e.getMessage();
        });
        final String value = completedFuture.get();
        log.info("返回值:{}", value);
    }
}
执行结果如下:
2022-08-27 10:21:44.440  INFO 44716 --- [-example-task-1] e.d.j.t.c.ExceptionallyTest              : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:21:44.440  INFO 44716 --- [-example-task-1] e.d.j.t.c.ExceptionallyTest              : exceptionally线程执行, 线程名称:demo-example-task-1
2022-08-27 10:21:44.444 ERROR 44716 --- [-example-task-1] e.d.j.t.c.ExceptionallyTest              : exceptionally线程执行, 得到supplyAsync中的异常堆栈信息
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) ~[na:1.8.0_151]
	at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) ~[na:1.8.0_151]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592) ~[na:1.8.0_151]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_151]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_151]
	at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_151]
Caused by: java.lang.ArithmeticException: / by zero
	at example.demo.jiangkd.thread.completablefuture.ExceptionallyTest.lambda$exceptionallyTest$0(ExceptionallyTest.java:45) ~[test-classes/:na]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590) ~[na:1.8.0_151]
	... 3 common frames omitted
2022-08-27 10:21:44.444  INFO 44716 --- [           main] e.d.j.t.c.ExceptionallyTest              : 返回值:exceptionally得到supplyAsync的异常信息, java.lang.ArithmeticException: / by zero
3.2.5.whenComplete
CompletableFuture的whenComplete方法表示,某个任务执行完成后,执行的回调方法,并且任务结果作为回调方法的入参,回调方法无返回值,并且whenComplete方法返回的CompletableFuture的result是第一个任务的结果。
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class WhenCompleteTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    UserService userService;
    @Test
    public void whenCompleteTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            return userService.getName();
        }, threadPoolTaskExecutor).whenComplete((value, throwable) -> {
            //
            log.info("whenComplete线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("whenComplete线程执行, 得到supplyAsync的返回结果:{}", value);
        });
        // 得到的value是supplyAsync的结果
        final String value = completableFuture.get();
        log.info("返回结果:{}", value);
    }
}
执行结果:
2022-08-27 10:30:58.969  INFO 47592 --- [-example-task-1] e.d.j.t.c.WhenCompleteTest               : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:30:59.981  INFO 47592 --- [-example-task-1] e.d.j.t.c.WhenCompleteTest               : whenComplete线程执行, 线程名称:demo-example-task-1
2022-08-27 10:30:59.981  INFO 47592 --- [-example-task-1] e.d.j.t.c.WhenCompleteTest               : whenComplete线程执行, 得到supplyAsync的返回结果:Tom
2022-08-27 10:30:59.982  INFO 47592 --- [           main] e.d.j.t.c.WhenCompleteTest               : 返回结果:Tom
3.2.6.handle
CompletableFuture的handle方法表示,某个任务执行完成后,执行回调方法,并且任务结果作为回调方法的入参,回调方法是有返回值的,并且handle方法返回的CompletableFuture的result就是回调方法执行的结果。
示例代码:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class HandleTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    UserService userService;
    @Test
    public void handleTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            return userService.getName();
        }, threadPoolTaskExecutor).handle((value, throwable) -> {
            //
            log.info("handle线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("handle线程执行, 得到supplyAsync的返回结果:{}", value);
            return "handle的返回结果" + value;
        });
        // 得到的value是handle的结果
        final String value = completableFuture.get();
        log.info("返回结果:{}", value);
    }
}
执行结果:
2022-08-27 10:35:50.796  INFO 41200 --- [-example-task-1] e.d.j.t.completablefuture.HandleTest     : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 10:35:51.812  INFO 41200 --- [-example-task-1] e.d.j.t.completablefuture.HandleTest     : handle线程执行, 线程名称:demo-example-task-1
2022-08-27 10:35:51.813  INFO 41200 --- [-example-task-1] e.d.j.t.completablefuture.HandleTest     : handle线程执行, 得到supplyAsync的返回结果:Tom
2022-08-27 10:35:51.813  INFO 41200 --- [           main] e.d.j.t.completablefuture.HandleTest     : 返回结果:handle的返回结果Tom
3.3.多个任务组合处理

3.3.1.AND组合关系
thenCombine/thenCombineAsync、thenAcceptBoth/thenAcceptBothAsync、runAfterBoth/runAfterBothAsync都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个指定任务。
区别在于:
- thenCombine/thenCombineAsync:会将两个任务的执行结果作为方法入参,传递到指定任务中,且指定方法有返回值。
- thenAcceptBoth/thenAcceptBothAsync:会将两个任务的执行结果作为方法入参,传递到指定任务中,且指定方法无返回值。
- runAfterBoth/runAfterBothAsync:不会把执行结果当做任务方法入参,也就是指定任务没有入参,且没有返回值。
thenCombine/thenCombineAsync示例代码:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class AndTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    @Resource
    UserService userService;
    /**
     * thenCombine/thenCombineAsync
     * <p>
     * 会将两个任务的执行结果作为方法入参,传递到指定任务中,且指定方法有返回值。
     */
    @Test
    public void thenCombineAndThenCombineAsyncTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> dateCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getDate线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第一个CompletableFuture的返回
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getName线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第二个CompletableFuture的返回
            return userService.getName();
        }, threadPoolTaskExecutor).thenCombine(dateCompletableFuture, (nameValue, dateValue) -> {
            //
            log.info("thenCombine线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("第一个CompletableFuture的执行结果:{}", dateValue);
            log.info("第二个CompletableFuture的执行结果:{}", nameValue);
            return "两个异步任务的组合结果";
        });
        final String value = completableFuture.get();
        log.info("返回结果:{}", value);
    }
}
thenCombine/thenCombineAsync执行结果:
2022-08-27 11:02:59.442  INFO 39580 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : supplyAsync getDate线程执行, 线程名称:demo-example-task-1
2022-08-27 11:02:59.442  INFO 39580 --- [-example-task-2] e.d.j.thread.completablefuture.AndTest   : supplyAsync getName线程执行, 线程名称:demo-example-task-2
2022-08-27 11:03:02.502  INFO 39580 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : thenCombine线程执行, 线程名称:demo-example-task-1
2022-08-27 11:03:02.502  INFO 39580 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : 第一个CompletableFuture的执行结果:现在的时间是2022-08-27 11:03:02
2022-08-27 11:03:02.502  INFO 39580 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : 第二个CompletableFuture的执行结果:Tom
2022-08-27 11:03:02.502  INFO 39580 --- [           main] e.d.j.thread.completablefuture.AndTest   : 返回结果:两个异步任务的组合结果
注意:示例代码中thenCombine的参数中,第一个参数是执行的第一个CompletableFuture异步任务对象,第二个参数是BiFunction对象,BiFunction的两个入参就是两个CompletableFuture的执行结果,第一个入参是第二个CompletableFuture的结果,第二个入参是第一个CompletableFuture(也就是代码中的dateCompletableFuture)的结果。
thenAcceptBoth/thenAcceptBothAsync示例代码如下:
    /**
     * thenAcceptBoth/thenAcceptBothAsync
     * <p>
     * 会将两个任务的执行结果作为方法入参,传递到指定任务中,且指定方法无返回值。
     */
    @Test
    public void thenAcceptBothAndthenAcceptBothAsyncTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> dateCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getDate线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第一个CompletableFuture的返回
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getName线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第二个CompletableFuture的返回
            return userService.getName();
        }, threadPoolTaskExecutor).thenAcceptBoth(dateCompletableFuture, (nameValue, dateValue) -> {
            //
            log.info("thenCombine线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("第一个CompletableFuture的执行结果:{}", dateValue);
            log.info("第二个CompletableFuture的执行结果:{}", nameValue);
            log.info("两个异步任务的组合结果");
        });
        final Void unused = completableFuture.get();
        log.info("返回结果:{}", unused);
    }
thenAcceptBoth/thenAcceptBothAsync执行结果:
2022-08-27 11:12:34.153  INFO 35832 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : supplyAsync getDate线程执行, 线程名称:demo-example-task-1
2022-08-27 11:12:34.153  INFO 35832 --- [-example-task-2] e.d.j.thread.completablefuture.AndTest   : supplyAsync getName线程执行, 线程名称:demo-example-task-2
2022-08-27 11:12:37.202  INFO 35832 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : thenCombine线程执行, 线程名称:demo-example-task-1
2022-08-27 11:12:37.202  INFO 35832 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : 第一个CompletableFuture的执行结果:现在的时间是2022-08-27 11:12:37
2022-08-27 11:12:37.202  INFO 35832 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : 第二个CompletableFuture的执行结果:Tom
2022-08-27 11:12:37.202  INFO 35832 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : 两个异步任务的组合结果
2022-08-27 11:12:37.202  INFO 35832 --- [           main] e.d.j.thread.completablefuture.AndTest   : 返回结果:null
runAfterBoth/runAfterBothAsync的示例代码如下:
    /**
     * runAfterBoth/runAfterBothAsync
     * <p>
     * 不会把执行结果当做任务方法入参,也就是指定任务没有入参,且没有返回值。
     */
    @Test
    public void runAfterBothAndRunAfterBothAsyncTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> dateCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getDate线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第一个CompletableFuture的返回
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getName线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第二个CompletableFuture的返回
            return userService.getName();
        }, threadPoolTaskExecutor).runAfterBoth(dateCompletableFuture, () -> {
            //
            log.info("thenCombine线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("两个异步任务的组合结果");
        });
        final Void unused = completableFuture.get();
        log.info("返回结果:{}", unused);
    }
runAfterBoth/runAfterBothAsync的执行结果
2022-08-27 11:15:25.858  INFO 19876 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : supplyAsync getDate线程执行, 线程名称:demo-example-task-1
2022-08-27 11:15:25.858  INFO 19876 --- [-example-task-2] e.d.j.thread.completablefuture.AndTest   : supplyAsync getName线程执行, 线程名称:demo-example-task-2
2022-08-27 11:15:28.924  INFO 19876 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : thenCombine线程执行, 线程名称:demo-example-task-1
2022-08-27 11:15:28.924  INFO 19876 --- [-example-task-1] e.d.j.thread.completablefuture.AndTest   : 两个异步任务的组合结果
2022-08-27 11:15:28.924  INFO 19876 --- [           main] e.d.j.thread.completablefuture.AndTest   : 返回结果:null
3.3.2.OR 组合的关系
applyToEither/applyToEitherAsync、acceptEither/acceptEitherAsync、runAfterEither/runAfterEitherAsync 都表示:将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个指定任务。
区别在于
- applyToEither/applyToEitherAsync:会将已经执行完成的任务的结果,作为方法入参,传递到指定任务中,且指定任务有返回值。
- acceptEither/acceptEitherAsync:会将已经执行完成的任务的结果,作为方法入参,传递到指定任务中,且指定任务无返回值。
- runAfterEither/runAfterEitherAsync:不会把执行结果当做指定任务的入参,且指定任务没有返回值。
applyToEither/applyToEitherAsync的示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class OrTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    DateService dateService;
    @Resource
    UserService userService;
    /**
     * applyToEither/applyToEitherAsync
     */
    @Test
    public void applyToEitherAndApplyToEitherTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> dateCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getDate线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第一个CompletableFuture的返回
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getName线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第二个CompletableFuture的返回
            return userService.getName();
        }, threadPoolTaskExecutor).applyToEither(dateCompletableFuture, value-> {
            //
            log.info("applyToEither线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("获取到某个CompletableFuture的执行结果:{}", value);
            return "获取到先执行完的CompletableFuture的结果";
        });
        final String value = completableFuture.get();
        log.info("返回结果:{}", value);
    }
}
applyToEither/applyToEitherAsync执行结果
2022-08-27 11:26:50.093  INFO 49780 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : supplyAsync getName线程执行, 线程名称:demo-example-task-2
2022-08-27 11:26:50.093  INFO 49780 --- [-example-task-1] e.d.j.thread.completablefuture.OrTest    : supplyAsync getDate线程执行, 线程名称:demo-example-task-1
2022-08-27 11:26:51.110  INFO 49780 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : applyToEither线程执行, 线程名称:demo-example-task-2
2022-08-27 11:26:51.110  INFO 49780 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : 获取到某个CompletableFuture的执行结果:Tom
2022-08-27 11:26:51.111  INFO 49780 --- [           main] e.d.j.thread.completablefuture.OrTest    : 返回结果:获取到先执行完的CompletableFuture的结果
注意:因为我们这里的DateService中的getDate方法睡眠了3s,而UserService的getName睡眠了1s, 所以每次执行一般都是getName先执行完,所以applyToEither获取到的总是第二个CompletableFuture的返回结果,你也可以改改睡眠时间试一试。
acceptEither/acceptEitherAsync的示例代码如下:
    /**
     * acceptEither/acceptEitherAsync
     */
    @Test
    public void acceptEitherAndAcceptEitherTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> dateCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getDate线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第一个CompletableFuture的返回
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getName线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第二个CompletableFuture的返回
            return userService.getName();
        }, threadPoolTaskExecutor).acceptEither(dateCompletableFuture, value-> {
            //
            log.info("applyToEither线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("获取到某个CompletableFuture的执行结果:{}", value);
        });
        final Void unused = completableFuture.get();
        log.info("返回结果:{}", unused);
    }
acceptEither/acceptEitherAsync执行结果:
2022-08-27 13:22:17.330  INFO 28552 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : supplyAsync getName线程执行, 线程名称:demo-example-task-2
2022-08-27 13:22:17.330  INFO 28552 --- [-example-task-1] e.d.j.thread.completablefuture.OrTest    : supplyAsync getDate线程执行, 线程名称:demo-example-task-1
2022-08-27 13:22:18.344  INFO 28552 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : applyToEither线程执行, 线程名称:demo-example-task-2
2022-08-27 13:22:18.344  INFO 28552 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : 获取到某个CompletableFuture的执行结果:Tom
2022-08-27 13:22:18.345  INFO 28552 --- [           main] e.d.j.thread.completablefuture.OrTest    : 返回结果:null
runAfterEither/runAfterEitherAsync的示例代码如下:
    /**
     * runAfterEither/runAfterEitherAsync
     */
    @Test
    public void runAfterEitherAndRunAfterEitherTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> dateCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getDate线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第一个CompletableFuture的返回
            return dateService.getDate();
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            //
            log.info("supplyAsync getName线程执行, 线程名称:{}", Thread.currentThread().getName());
            // 第二个CompletableFuture的返回
            return userService.getName();
        }, threadPoolTaskExecutor).runAfterEither(dateCompletableFuture, () -> {
            //
            log.info("applyToEither线程执行, 线程名称:{}", Thread.currentThread().getName());
        });
        final Void unused = completableFuture.get();
        log.info("返回结果:{}", unused);
    }
runAfterEither/runAfterEitherAsync的执行结果:
2022-08-27 13:25:07.001  INFO 51980 --- [-example-task-1] e.d.j.thread.completablefuture.OrTest    : supplyAsync getDate线程执行, 线程名称:demo-example-task-1
2022-08-27 13:25:07.001  INFO 51980 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : supplyAsync getName线程执行, 线程名称:demo-example-task-2
2022-08-27 13:25:08.010  INFO 51980 --- [-example-task-2] e.d.j.thread.completablefuture.OrTest    : applyToEither线程执行, 线程名称:demo-example-task-2
2022-08-27 13:25:08.010  INFO 51980 --- [           main] e.d.j.thread.completablefuture.OrTest    : 返回结果:null
3.4.AllOf
所有任务都执行完成后,才执行 allOf返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture,执行get方法,会抛出异常。
和上面3.3.1.AND组合关系不同,这里的allOf方法可以是多个CompletableFuture完成后再执行某个依赖线程,而AND组合只是两个CompletableFuture。
allOf的示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class AllOfTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Test
    public void allOfTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync1线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "completableFuture1";
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync2线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "completableFuture2";
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<String> c3 = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync3线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "completableFuture3";
        }, threadPoolTaskExecutor);
        final CompletableFuture<Void> completableFuture = CompletableFuture.allOf(c1, c2, c3).thenRun(() -> {
            log.info("thenRun线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("c1, c2, c3全部执行完毕, 执行了thenRun线程");
        });
        final Void unused = completableFuture.get();
        log.info("返回值:{}", unused);
    }
}
执行结果
2022-08-27 13:46:57.905  INFO 50628 --- [-example-task-3] e.d.j.t.completablefuture.AllOfTest      : supplyAsync3线程执行, 线程名称:demo-example-task-3
2022-08-27 13:46:57.905  INFO 50628 --- [-example-task-2] e.d.j.t.completablefuture.AllOfTest      : supplyAsync2线程执行, 线程名称:demo-example-task-2
2022-08-27 13:46:57.905  INFO 50628 --- [-example-task-1] e.d.j.t.completablefuture.AllOfTest      : supplyAsync1线程执行, 线程名称:demo-example-task-1
2022-08-27 13:46:57.907  INFO 50628 --- [-example-task-1] e.d.j.t.completablefuture.AllOfTest      : thenRun线程执行, 线程名称:demo-example-task-1
2022-08-27 13:46:57.907  INFO 50628 --- [-example-task-1] e.d.j.t.completablefuture.AllOfTest      : c1, c2, c3全部执行完毕, 执行了thenRun线程
2022-08-27 13:46:57.907  INFO 50628 --- [           main] e.d.j.t.completablefuture.AllOfTest      : 返回值:null
3.5.AnyOf
任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行的任务异常,anyOf的CompletableFuture执行get方法,会抛出异常。
anyOf的示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class AnyOfTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Test
    public void anyOfTest() throws ExecutionException, InterruptedException {
        //
        final CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync1线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "completableFuture1";
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync2线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "completableFuture2";
        }, threadPoolTaskExecutor);
        //
        final CompletableFuture<String> c3 = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync3线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "completableFuture3";
        }, threadPoolTaskExecutor);
        final CompletableFuture<Object> completableFuture = CompletableFuture.anyOf(c1, c2, c3).whenComplete((value, Throwable) -> {
            //
            log.info("whenComplete线程执行, 线程名称:{}", Thread.currentThread().getName());
            log.info("c1, c2, c3任意一个执行完毕, 执行了whenComplete线程, 获取执行完的线程的结果值:{}", value);
        });
        final Object value = completableFuture.get();
        log.info("返回值:{}", value);
    }
}
执行结果:
2022-08-27 13:57:58.395  INFO 50456 --- [-example-task-3] e.d.j.t.completablefuture.AnyOfTest      : supplyAsync3线程执行, 线程名称:demo-example-task-3
2022-08-27 13:57:58.395  INFO 50456 --- [-example-task-2] e.d.j.t.completablefuture.AnyOfTest      : supplyAsync2线程执行, 线程名称:demo-example-task-2
2022-08-27 13:57:58.395  INFO 50456 --- [-example-task-1] e.d.j.t.completablefuture.AnyOfTest      : supplyAsync1线程执行, 线程名称:demo-example-task-1
2022-08-27 13:57:58.396  INFO 50456 --- [-example-task-3] e.d.j.t.completablefuture.AnyOfTest      : whenComplete线程执行, 线程名称:demo-example-task-3
2022-08-27 13:57:58.396  INFO 50456 --- [-example-task-3] e.d.j.t.completablefuture.AnyOfTest      : c1, c2, c3任意一个执行完毕, 执行了whenComplete线程, 获取执行完的线程的结果值:completableFuture3
2022-08-27 13:57:58.396  INFO 50456 --- [           main] e.d.j.t.completablefuture.AnyOfTest      : 返回值:completableFuture3
3.6.thenCompose
thenCompose方法会在某个任务执行完成后,将该任务的执行结果作为方法入参,去执行指定的方法。该指定方法会返回一个新的CompletableFuture实例。
- 如果该CompletableFuture实例的result不为null,则thenCompose返回一个基于该result新的CompletableFuture实例。
- 如果该CompletableFuture实例为null,然后就执行这个thenCompose新任务。
示例代码如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoExampleSpringbootApplication.class)
public class ThenComposeTest {
    @Resource
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Test
    public void thenComposeTest() throws ExecutionException, InterruptedException {
        // 获取thenCompose返回的CompletableFuture
        final CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "Hello";
        }, threadPoolTaskExecutor).thenCompose(value -> {
            return CompletableFuture.supplyAsync(() -> {
                log.info("thenCompose中获取supplyAsync的返回值:{}", value);
                return value + " World!";
            });
        });
        final String value = completableFuture.get();
        log.info("返回值:{}", value);
        log.info("=======================================================");
        // 只是执行thenCompose
        CompletableFuture.supplyAsync(() -> {
            log.info("supplyAsync线程执行, 线程名称:{}", Thread.currentThread().getName());
            return "Hello";
        }, threadPoolTaskExecutor).thenCompose(v -> {
            log.info("只是打印一下supplyAsync的返回值:{}", v);
            return null;
        });
    }
}
执行结果:
2022-08-27 14:39:16.178  INFO 5216 --- [-example-task-1] e.d.j.t.c.ThenComposeTest                : supplyAsync线程执行, 线程名称:demo-example-task-1
2022-08-27 14:39:16.180  INFO 5216 --- [onPool-worker-9] e.d.j.t.c.ThenComposeTest                : thenCompose中获取supplyAsync的返回值:Hello
2022-08-27 14:39:16.180  INFO 5216 --- [           main] e.d.j.t.c.ThenComposeTest                : 返回值:Hello World!
2022-08-27 14:39:16.181  INFO 5216 --- [           main] e.d.j.t.c.ThenComposeTest                : =======================================================
2022-08-27 14:39:16.181  INFO 5216 --- [-example-task-2] e.d.j.t.c.ThenComposeTest                : supplyAsync线程执行, 线程名称:demo-example-task-2
2022-08-27 14:39:16.181  INFO 5216 --- [-example-task-2] e.d.j.t.c.ThenComposeTest                : 只是打印一下supplyAsync的返回值:Hello
4.CompletableFuture使用注意点
CompletableFuture 使我们的异步编程更加便利的、代码更加优雅的同时,我们也要关注下它,使用的一些注意点。

4.1.Future需要获取返回值,才能获取异常信息
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
      int a = 0;
      int b = 68;
      int c = b / a;
      return true;
   }, executorService).thenAccept(System.out::println);
 // 如果不加 get()方法这一行,看不到异常信息
 future.get();
Future需要获取返回值,才能获取到异常信息。如果不加 get()/join()方法,看不到异常信息。所以使用的时候,注意一下考虑是否加try...catch...或者使用exceptionally方法。
4.2.CompletableFuture的get()方法是阻塞的
CompletableFuture的get()方法是阻塞的,如果使用它来获取异步调用的返回值,需要添加超时时间。
// 阻塞
 CompletableFuture.get();
// 添加超时时间
CompletableFuture.get(5, TimeUnit.SECONDS);
4.3.线程池的注意点
CompletableFuture代码中可能会使用默认的线程池。在大量请求过来的时候,处理逻辑复杂的话,响应会很慢。一般建议使用自定义线程池,优化线程池配置参数。
4.4.自定义线程池注意饱和策略
CompletableFuture的get()方法是阻塞的,我们一般建议使用future.get(3, TimeUnit.SECONDS)。并且一般建议使用自定义线程池。
但是如果线程池拒绝策略是DiscardPolicy或者DiscardOldestPolicy,当线程池饱和时,会直接丢弃任务,不会抛弃异常。因此建议,CompletableFuture线程池策略最好使用AbortPolicy,然后耗时的异步线程,做好线程池隔离。
CompletableFuture使用方法的详细说明的更多相关文章
- java中的compareto方法的详细介绍
		java中的compareto方法的详细介绍 Java Comparator接口实例讲解(抽象方法.常用静态/默认方法) 一.java中的compareto方法 1.返回参与比较的前后两个字符串的as ... 
- Spring Boot 日志配置方法(超详细)
		默认日志 Logback : 默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台.在运行应用程序和其他例子时,你应该已经看到很多INFO级别的日志了. 从上图 ... 
- log4php的使用方法与详细配置
		log4php的使用 首先引入logger.php文件.log4php可以通过引入logger.php来完成自动加载的过程.文件位置如下: 日志记录器自身没有定义日志的输出目的地和格式,所以我们通常需 ... 
- Session中load/get方法的详细区别
		Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象.其区别在于: 如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个O ... 
- 电子表格控件Spreadsheet 对象方法事件详细介绍
		1.ActiveCell:返回代表活动单元格的Range只读对象.2.ActiveSheet:返回代表活动工作表的WorkSheet只读对象.3.ActiveWindow:返回表示当前窗口的Windo ... 
- C#调用Java方法(详细实例)
		C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具体例子把一个JAVA方法转换成可以由C#直接调用的DLL C#调用c++ C#调用 ... 
- java中的反射机制,以及如何通过反射获取一个类的构造方法 ,成员变量,方法,详细。。
		首先先说一下类的加载,流程.只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制. 一. 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三 ... 
- jQuery Ajax 方法调用 Asp.Net WebService 以及调用aspx.cs中方法的详细例子
		一.jQuery Ajax 方法调用 Asp.Net WebService (引自Terry Feng) Html文件 <!DOCTYPE html PUBLIC "-//W3C//D ... 
- jQuery each的实现与call方法的详细介绍
		转载原出处: http://www.f2es.com/jquery-each-intro/ 先贴上jquery实现each功能的源代码(把常用的call部分提取出来,为了方便理解,就进行了一定的修改) ... 
- 关于sharepoint 2010无法显示用户中文名的解决方法和详细剖析
		相信这个问题许多做sharepoint的朋友都曾经遇到过,就是本来很正常的中文用户名莫名其妙的变成了“域名\账号”,我本人也遇到过好多次,每次都是百度谷歌一下草草解决问题,始终也没真正去弄明白是怎么回 ... 
随机推荐
- 1月3日内容总结——bbs项目登陆页面和主页、个人站点页的搭建
			目录 一.登陆功能完善 验证码功能实现 单机验证码实现验证码刷新(局部刷新) 点击登陆提交数据进行校验 二.主页搭建 html代码 views.py代码 主页内容部分 后台添加数据 分页器 前端获取头 ... 
- XMind 2022 安装教程 (11-30亲测有效)
			下载地址 下载直通车:立即下载 解压文件 鼠标右击[XMind2022(64bit)]压缩包(win11系统需先点击"显示更多选项")选择[解压到 XMind2022(64bit) ... 
- 鼎阳SDS6204示波器的EPICS IOC调试
			经过雷雷师弟的努力,该款示波器终于调试成功,相关文件现放在gitee仓库里: https://gitee.com/lup9304/siglent/commit/99ce00d195facd87fa1c ... 
- 大规模 IoT 边缘容器集群管理的几种架构-1-Rancher+K3s
			前文回顾 大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介 ️Reference: IoT 边缘计算系列文章 Rancher + K3s 简介 Rancher: Kubernetes ... 
- 安卓逆向.5 xposed 替换方法-(实战贪吃蛇)
			代码剖析 调用成功方法 获取类 方法写入 实战例子(贪吃蛇) 
- xampp修改mysql数据库密码(测试成功)
			转载: http://www.360doc.com/content/17/0608/14/8797027_661063783.shtml ------------------------------- ... 
- 跳板攻击之:Netsh端口代理转发
			跳板攻击之:Netsh端口代理转发 目录 跳板攻击之:Netsh端口代理转发 1 命令解析 2 代理转发内网22端口 3 代理转发外网4444端口 4 注意 1 命令解析 netsh interfac ... 
- 04#Web 实战:Gitee 贡献图
			前言 这次要做的 Web 前端实战是一个 Gitee 个人主页下的贡献图(在线 Demo),偶尔做一两个,熟悉熟悉 JS 以及 jQ.整体来说这个案例并不难,主要是控制第一个节点以及最后一个节点处于星 ... 
- 通过 Pulsar 源码彻底解决重复消费问题
			背景 最近真是和 Pulsar 杠上了,业务团队反馈说是线上有个应用消息重复消费. 而且在测试环境是可以稳定复现的,根据经验来看一般能稳定复现的都比较好解决. 定位问题 接着便是定位问题了,根据之前的 ... 
- 基于C++的OpenGL 10 之光照贴图
			1. 引言 本文基于C++语言,描述OpenGL的光照贴图 前置知识可参考: 基于C++的OpenGL 09 之材质 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com) 笔者这里不过多描 ... 
