CompletableFuture快速入门

1,709 阅读3分钟

用处:异步任务编排,JDK8提供。

底层涉及到线程池,用的是jdk的线程池,可以在启动的时候设置。

修改参数具体可以根据电脑的核心大小。

但是建议不要修改线程池的大小参数等等。新建一个线程池,方法调用的时候传递即可。

主要API

1. 异步任务的创建

  • runAsync:运行一个异步任务,不返回结果。
  • supplyAsync:运行一个异步任务,返回结果。

2. 任务的组合

  • thenApply:当一个 CompletableFuture 完成时,应用一个函数转换结果。
  • thenAccept:当一个 CompletableFuture 完成时,消费其结果。
  • thenRun:当一个 CompletableFuture 完成时,运行一个 Runnable 任务。
  • thenCombine:当两个 CompletableFuture 完成时,组合它们的结果。
  • thenCompose:当一个 CompletableFuture 完成时,返回另一个 CompletableFuture

在组合API中,每一个都会有其他变种的api

看名字可以得出结论

xxxAsync将会异步执行

重载方法xxxAsync可以指定执行的线程池

3. 异常处理

  • exceptionally:处理任务执行中遇到的异常。
  • handle:无论任务是否正常完成,都可以处理结果或异常。

4. 结果获取

  • get:阻塞并获取结果。
  • join:类似于 get,但不抛出检查异常。

API使用举例

开启异步任务,捕获异常,结果获取

假设你是小王,来到一家餐厅吃饭,主要经历的事情应该是这样的

  1. 坐下,叫来服务员点菜
  2. 点完菜,服务员将菜单给厨师,厨师开始做菜
  3. 厨师做好菜,装饭,上菜给小王吃
  4. 小王吃完,结账走人

对应到代码就是下面这样的。

public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("小王来到餐厅,开始点菜了");
        CompletableFuture<String> dish= CompletableFuture.supplyAsync(() -> {
            log.info("开始做菜了");
            return "番茄炒蛋做好了,上菜";
        }).exceptionally((ex)->{
            String message = ex.getMessage();
            log.info(message);
            return message;
        });
        System.out.println(dish.get());//注意,该方法会抛出异常检查
    }

可以看到supplyAsync开启了做菜这个异步任务,并且返回一个String。

与此对应的还有runAsync这个方法,只不过该方法不需要返回值。

任务1->任务2

还是上面那个场景,只不过厨师在做菜的时候需要先配菜,才能将配菜做成菜品。

使用thenApply转换成代码如下

public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("小王来到餐厅,开始点菜了");
        CompletableFuture<String> dish= CompletableFuture.supplyAsync(() -> {
            log.info("开始做菜了");
            return "番茄炒蛋配菜";
        }).thenApplyAsync((garnish)->{//将上一步配菜传递到这边
            log.info("这是准备好的配菜{}",garnish);
            return "成品菜";
        }).exceptionally((ex)->{
            String message = ex.getMessage();
            log.info(message);
            return message;
        });
        System.out.println(dish.get());
    }

使用thenCompose转换成代码如下

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("小王来到餐厅,开始点菜了");
        CompletableFuture<String> dish = CompletableFuture.supplyAsync(() -> {
            log.info("开始做菜了");
            return "番茄炒蛋配菜";
        }).thenCompose((garnish) -> {
            return CompletableFuture.supplyAsync(() -> {
                log.info("这是准备好的配菜: " + garnish);
                return "成品菜";
            });
        }).exceptionally((ex) -> {
            String message = ex.getMessage();
            log.info("遇到异常: " + message);
            return "烹饪失败: " + message;
        });

        // 阻塞等待结果
        String result = dish.get();
        log.info("最终结果: " + result);
    }

两者的区别在于,thenCompose是将一个CompletableFuture转换成另外一个。而thenApply是任务级别之间的传递。

任务1&任务2

thenCombine 方法用于当两个 CompletableFuture 都完成时,组合它们的结果。

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        log.info("小王来到餐厅,开始点菜了");
        CompletableFuture<String> riceFuture = CompletableFuture.supplyAsync(() -> {
            log.info("开始做饭");
            return "蒸饭完成";
        });

//       参数列表 supplier2,function
        CompletableFuture<String> dishFuture = riceFuture.thenCombine(
                CompletableFuture.supplyAsync(() -> {
                    log.info("开始做菜");
                    return "番茄炒蛋";
                }),
                (rice, dish) -> {
                    log.info("组合结果");
                    return rice + " 和 " + dish;
                }
        ).exceptionally((ex) -> {
            String message = ex.getMessage();
            log.info("遇到异常: " + message);
            return "烹饪失败: " + message;
        });

        // 阻塞等待结果
        String result = dishFuture.get();
        log.info("最终结果: " + result);
    }