侧边栏壁纸
  • 累计撰写 58 篇文章
  • 累计创建 67 个标签
  • 累计收到 1 条评论

多线程|CompletableFuture

lihaocheng
2022-05-09 / 0 评论 / 0 点赞 / 819 阅读 / 2,295 字
温馨提示:
晚上记得开启夜间模式哦

一、快速上手 CompletableFuture

1. 一个简单的 CompletableFuture Demo

CompletableFuture<Integer> test=CompletableFuture.supplyAsync(()-> func());

其中,func()是一个返回值为Integer的函数。
我们可以通过join()方法来获取 func() 的返回值。

//这里可以运行其他的代码
Integer num=test.join();

此时这个 num 值,就是我们通过异步来获取的。这里我们就完成了最简单的 CompletableFuture 的使用案例。

到这里,肯定会有人觉得,就这?

别着急,到这里我们能确定一个事情——这里启动了另外一个线程来进行异步处理。最后通过 join() 来获取另外一个线程的处理结果。

由此,今天很重要的两个问题产生了

  1. 另外一个线程哪里来的?
  2. join() 的时候另外一个线程没有处理完会发生什么?

让我们来一步一步的解决这两个问题

2.CompletableFuture 初始化参数

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(ASYNC_POOL, supplier);
    }

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
    }

    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(ASYNC_POOL, runnable);
    }

    public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }

从上面的源码中可以看出 CompletableFuture 提供了四个初始化的方法,runAsyncsupplyAsync 分别提供了 RunnableSupplier<U> 的参数选择。同时,还提供了带线程池参数的方法,可以指定线程池。

//使用默认线程池
static CompletableFuture<Void> 
  runAsync(Runnable runnable)
static <U> CompletableFuture<U> 
  supplyAsync(Supplier<U> supplier)
//可以指定线程池  
static CompletableFuture<Void> 
  runAsync(Runnable runnable, Executor executor)
static <U> CompletableFuture<U> 
  supplyAsync(Supplier<U> supplier, Executor executor)  

而不带线程池的创建方法,使用了名为 ASYNC_POOL 的线程池。
我们打上断点,看下线程池是从哪里来的。

截屏20220714 00.16.04.png
我们可以看到他使用 ForkJoinPool 的线程池。

ASYNC_POOL = (Executor)(USE_COMMON_POOL ? ForkJoinPool.commonPool() : new CompletableFuture.ThreadPerTaskExecutor());

3.join 方法

join 方法和 Future 接口中的 get 方法有相同的含义,并
且也声明在 Future 接口中,它们唯一的区别是 CompletableFuture 的 join() 不会抛出任何检测到的异常。因此此时如果 join() 没有处理完,并不会抛出任何异常。

二、CompletableFuture 提供的方法

除了我们上面说到的 join() CompletableFuture 还提供了,像方法

三、CompletableFuture 其他用法

1.在stream 中使用 CompletableFuture

List<CompletableFuture<String>> priceFutures =
                shops.stream()
                        .map(shop -> CompletableFuture.supplyAsync(
                                () -> shop.getName() + " price is " +
                                        shop.getPrice(product)))
                        .collect(Collectors.toList());
        return priceFutures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());

四、总结

  1. CompletableFuture 使用时最好指定线程池。不指定默认会使用 ForkJoinPool 作为线程池。当然如果 CPU 数量小于 2 会直接 new Thread ,这会导致 FULL GC 问题。
  2. join 方法不会报错,但是会阻塞 join 后面的任务
0

评论区