CompletableFuture学习笔记

126 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

一、CompletableFuture介绍

CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。

CompletableFuture实现了俩接口。这个是Future的实现类。使用completionStage接口去支持完成时触发的函数和操作。

image.png

CompletionStage接口定义了任务编排的方法,执行某一阶段,可以向下执行后续阶段。异步执行的,默认线程池是ForkJoinPool.commonPool(),但为了业务之间互不影响,且便于定位问题,强烈推荐使用自定义线程池

默认线程池如下:

// 根据commonPool的并行度来选择,而并行度的计算是在ForkJoinPool的静态代码段完成的
private static final boolean useCommonPool =
    (ForkJoinPool.getCommonPoolParallelism() > 1);

private static final Executor asyncPool = useCommonPool ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

二、CompletableFuture方法介绍

1、获取返回的结果

  • get();
  • getNow() 这个方法是执行这个方法的时候任务执行完了就返回任务的结果,如果任务没有执行完就返回你的入参

2、异步操作

  • supply是持有返回值的:completetableFuture.supplyAsync
  • run是void返回值:completetableFuture.runAsync
    public static CompletableFuture<Void> runAsync(Runnable runnable)
    public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

指定一个线程池:

ExecutorService executor = Executors.newCachedThreadPool();
CompletableFuture<Void> runFuture = CompletableFuture.runAsync(() -> System.out.println("run,关注"), executor);

public DeferredResult<Result> get() {
DeferredResult<Result> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(() -> {
            return 阻塞数据库IO;
        //dbThreadPool用来处理阻塞的数据库IO
        }, dbThreadPool).thenComposeAsync(result -> {
    deferredResult.setResult(result);
});
return deferredResult;
}

3、并行执行

  • completetableFuture.allOf入参是一个completableFuture组、allOf就是所有任务都完成时返回。但是是个Void的返回值。
  • completetableFuture.anyOf入参的completableFuture组中有一个任务执行完毕就返回。返回结果是第一个完成的任务的结果。

4、join

completetableFuture.join方法跟线程的join用法差不多,将主线程阻塞。

5、结果处理

completetableFuture.whenComplete 任务执行完毕调用的,传入一个action,这个方法的执行线程是当前线程,意味着会阻塞当前线程。 completetableFuture.whenCompleteAsync completetableFuture.whenCompleteAsync用自定义Executor

6、依赖关系,结果转换,将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果

completetableFuture.then方法瞅着挺多的。实际上就是异不异步和加不加自定义Executor. completetableFuture.thenCompose就是一个任务执行完之后可以用它的返回结果接着执行的方法。方法返回的是另一个你期盼泛型的结果

CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(30);
        System.out.println("第一次运算:" + number);
        return number;
    }
})
.thenCompose(new Function<Integer, CompletionStage<Integer>>() {
    @Override
    public CompletionStage<Integer> apply(Integer param) {
        return CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                int number = param * 2;
                System.out.println("第二次运算:" + number);
                return number;
            }
        });
    }
});

completetableFuture.thenCombine。这个combine的理解就是结合两个任务的结果。

CompletableFuture<Integer> future1 = CompletableFuture
    .supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            int number = new Random().nextInt(10);
            System.out.println("任务1结果:" + number);
            return number;
        }
    });
CompletableFuture<Integer> future2 = CompletableFuture
    .supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            int number = new Random().nextInt(10);
            System.out.println("任务2结果:" + number);
            return number;
        }
    });
CompletableFuture<Integer> result = future1
    .thenCombine(future2, new BiFunction<Integer, Integer, Integer>() {
        @Override
        public Integer apply(Integer x, Integer y) {
            return x + y;
        }
    });
System.out.println("组合后结果:" + result.get());

completetableFuture.thenRun就是这个任务运行完,再运行下一个任务 completetableFuture.thenApply(Function);这样的就是有入参有返回值类型的。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    int result = 100;
    System.out.println("第一次运算:" + result);
    return result;
}).thenApply(number -> {
    int result = number * 3;
    System.out.println("第二次运算:" + result);
    return result;
});

completetableFuture.thenAccept(Consumer);这样的就是有入参,但是没有返回值的

CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> {
    int number = new Random().nextInt(10);
    System.out.println("第一次运算:" + number);
    return number;
}).thenAccept(number ->
              System.out.println("第二次运算:" + number * 5));