CompletableFuture 的 allOf 方法底层原理是什么

本文深入剖析了Java并发库CompletableFuture中allOf和anyOf方法的源码实现,通过实例和流程分析,揭示了这两个方法如何协调异步任务,以及它们在任务编排中的工作原理。文章详细解释了CompletableFuture对象的构造、任务提交、对象编排、join方法的阻塞与唤醒等关键步骤,帮助读者理解CompletableFuture的核心机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CompletableFuture 讲解系列



前言

CompletableFuture估计多数程序员都了解那么一丢丢,
网上介绍 API 的文章一大把,至于原理么,呵呵,不讲。

能把源码讲清楚的文章,约等于 0,甚少我一直没找到。
Doug Lea 的代码,可谓鬼斧神工,精妙绝伦,难懂!

今儿在这儿,我壮胆写篇文章,剖析下源码,探究下原理。
各位路过的大佬儿,还请多多指教。若有谬误,烦请指正。


一、CompletableFuture 是什么?

CompletableFuture 可以简单理解为 Future 的升级版,可以非常方便的进行任务的编排。

以往任务编排,需要借助 CountDownLatch、Semaphore、CyclicBarrier 等来实现,处理逻辑较为复杂。

本文源码层面分析 allOfanyOf 这两个方法。抽丝剥茧,深入剖析 CompletableFuture 的原理。

二、代码示例

1. allOf(CompletableFuture<?>... cfs)

allOf : 可以探测,所有的异步任务,是否全部执行完了。
举个例子,集齐七颗龙珠可以召唤神龙。
就可以分七个支线,每条支线找一颗龙珠。

// 不用CompletableFuture版本 

    public static void main(String[] args) throws InterruptedException {
   
        ExecutorService pool = Executors.newCachedThreadPool();
        long start = System.currentTimeMillis();
        int count = 7;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
   
            int num = i + 1;
            pool.submit(() -> {
   
                new Dragon(num).findPreciousness();
                latch.countDown();
            });
        }
        String mainThread = Thread.currentThread().getName();
        System.out.println(mainThread + "收集龙珠,等待中……");
        latch.await();
        long end = System.currentTimeMillis() - start;
        System.out.println(mainThread + "线程历经" + end + "毫秒,终于集齐七颗龙珠,开始召唤神龙!!");
        pool.shutdown();
    }

    static class Dragon {
   

        private int num;

        Dragon(int num) {
   
            this.num = num;
        }

        private void findPreciousness() {
   
            int cost = RandomUtil.randomInt(1, num + 1) * 1000;
            try {
   
                Thread.sleep(cost);
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "线程历经 " + cost + " 毫秒找到第 " + num + " 颗龙珠");
        }
    }

这个异步版本,用 CountDownLatch 来保证,所有异步任务都执行完。

如果不知道它怎么用,可以看我之前的文章《CountDownLatch源码分析》

集齐七颗龙珠的任务,交给线程池异步处理,

当子任务全部完成时,主线程继续执行,否则阻塞。

再看下,用 CompletableFutureallOf 方法实现的版本

    public static void main(String[] args) throws InterruptedException {
   
        ExecutorService pool = Executors.newCachedThreadPool();
        long start = System.currentTimeMillis();
        int count = 7;
        CompletableFuture[] temp = new CompletableFuture[count];
        for (int i = 0; i < count; i++) {
   
            int num = i + 1;
            temp[num - 1] = CompletableFuture.runAsync(() -> new Dragon(num).findPreciousness(), pool);
        }
        String mainThread = Thread.currentThread().getName();
        System.out.println(mainThread + "收集龙珠,等待中……");
        CompletableFuture.allOf(temp).join();
        long end = System.currentTimeMillis() - start;
        System.out.println(mainThread + "线程历经" + end + "毫秒,终于集齐七颗龙珠,开始召唤神龙!!");
        pool.shutdown();
    }

示例中 allOf 方法,实现了 CountDownLatch 的功能。

当然,如果你对 stream 写法很熟悉,那代码可以很简单。

    public static void main(String[] args) throws InterruptedException {
   
        ExecutorService pool = Executors.newCachedThreadPool();
        CompletableFuture[] futures = IntStream.rangeClosed(1, 7)
                .mapToObj(num -> new Dragon(num))
                .map(dragon -> CompletableFuture.runAsync(dragon::findPreciousness, pool))
                .toArray(size -> new CompletableFuture[size]);
        CompletableFuture.allOf(futures).join();
        pool.shutdown();
    }

2. anyOf(CompletableFuture<?>... cfs)

anyOf : 所有的异步任务中,任意一个完成了,就可以被探测到。
举个例子,集齐七颗龙珠可以召唤神龙,这个任务比较难。
找到任意一颗,就摆两桌庆祝下,不管是哪个支线找到的。

    public static void main(String[] args) throws InterruptedException {
   
        ExecutorService pool = Executors.newCachedThreadPool();
        CompletableFuture[] futures = IntStream.rangeClosed(1, 7)
                .mapToObj(num -> new Dragon(num))
                .map(dragon -> CompletableFuture.runAsync(dragon::findPreciousness, pool))
                .toArray(size -> new CompletableFuture[size]);
        CompletableFuture.anyOf(futures).join();
        pool.shutdown();

只需要修改一行代码,就可以了。把 allOf 改为 anyOf 即可。

同样用 CountDownLatch 也可以实现,也是改一行

CountDownLatch latch = new CountDownLatch(1);

CountDownLatch 是怎么实现的,不是本文的重点,不展开说了。

有兴趣的话,可以看我之前的文章《CountDownLatch源码分析》。

三、allOf 原理分析

1. 整体流程解释

在这里插入图片描述
截图中的1,指主线程提交任务到线程池。

截图中的2,指主线程调用 allOf 方法,生成 一个 CompletableFuture 对象 allOfFuture

线程池的工作线程,会执行异步任务,所有异步任务执行完,会给刚刚那个 allOfFuture 对象打个标识。

截图中的3,是主线程会判断那个标识,标识存在,说明所有异步任务已执行完。

若标识不存在,主线程阻塞,等待所有异步任务执行结束时,主线程会被唤醒。

以上是基本的原理,很重要。网上的文章很少会介绍这个。

我是前前后后,看了好多遍才想明白的,我也想找现成的文章看,可是压根就没有。

先有个印象,接着一条一条详细说。

2. runAsync 任务提交


  public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                 Executor executor) {
   
      return asyncRunStage(screenExecutor(executor), runnable);
  }
  
  static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) {
   
      if (f == null) throw new NullPointerExceptio
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值