如何正确等待 CompletableFuture 完成所有异步任务

来源:AI视频音频作者:相泽南头衔:网络博主
导读:本期聚焦于小伙伴创作的《如何正确等待 CompletableFuture 完成所有异步任务》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何正确等待 CompletableFuture 完成所有异步任务》有用,将其分享出去将是对创作者最好的鼓励。

在Java的异步编程中,CompletableFuture是处理异步任务的核心类,当我们需要同时提交多个异步任务,并且要等待所有任务都执行完成后再进行后续操作时,需要选择正确的等待方式,否则可能出现线程阻塞、异常无法捕获或者程序逻辑不符合预期的问题。

如何正确等待 CompletableFuture 完成所有异步任务

使用allOf结合join等待所有任务

CompletableFuture.allOf是专门用来等待多个CompletableFuture任务全部完成的方法,它接收一个CompletableFuture数组作为参数,返回一个新的CompletableFuture<Void>,当所有传入的任务都完成时,这个新的CompletableFuture才会完成。

如果需要获取所有任务的执行结果,或者需要阻塞当前线程等待所有任务完成,可以结合join方法使用,join方法会阻塞当前线程直到任务完成,和get方法不同的是,它不会抛出受检异常,更适合在lambda表达式中使用。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureWaitDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        // 创建三个异步任务
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务1完成";
        }, executor);

        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务2完成";
        }, executor);

        CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(800);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务3完成";
        }, executor);

        // 等待所有任务完成
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2, task3);
        // 阻塞等待,获取各个任务的结果
        allTasks.join();
        try {
            String result1 = task1.get();
            String result2 = task2.get();
            String result3 = task3.get();
            System.out.println("所有任务结果:" + result1 + "," + result2 + "," + result3);
        } catch (Exception e) {
            e.printStackTrace();
        }
        executor.shutdown();
    }
}

使用allOf结合get等待所有任务

get方法和join方法类似,也可以阻塞当前线程等待任务完成,但是get方法会抛出InterruptedExceptionExecutionException两个受检异常,使用时需要进行异常捕获或者声明抛出。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureGetDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        CompletableFuture<Integer> taskA = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100;
        }, executor);

        CompletableFuture<Integer> taskB = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(700);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 200;
        }, executor);

        CompletableFuture<Void> allTaskFuture = CompletableFuture.allOf(taskA, taskB);
        // 使用get方法等待,需要捕获异常
        allTaskFuture.get();
        System.out.println("任务A结果:" + taskA.get());
        System.out.println("任务B结果:" + taskB.get());
        executor.shutdown();
    }
}

等待时的异常处理

当多个异步任务中有任务执行抛出异常时,allOf返回的CompletableFuture会在所有任务完成后,将异常传递出来。如果使用join方法,会抛出CompletionException,如果使用get方法,会抛出ExecutionException,异常原因可以通过getCause获取。

如果希望某个任务异常时不影响其他任务的等待,可以在单个任务中使用exceptionally方法处理异常,返回默认值,这样allOf等待时不会因为单个任务异常而中断。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletableFutureExceptionDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        // 正常任务
        CompletableFuture<String> normalTask = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "正常任务结果";
        }, executor);

        // 会抛出异常的任务,使用exceptionally处理
        CompletableFuture<String> errorTask = CompletableFuture.supplyAsync(() -> {
            throw new RuntimeException("任务执行出错");
        }, executor).exceptionally(ex -> {
            System.out.println("捕获到任务异常:" + ex.getMessage());
            return "默认结果";
        });

        CompletableFuture<Void> allTasks = CompletableFuture.allOf(normalTask, errorTask);
        allTasks.join();
        System.out.println("正常任务结果:" + normalTask.join());
        System.out.println("异常任务结果:" + errorTask.join());
        executor.shutdown();
    }
}

不同等待方式的对比

我们可以通过下表对比几种常见等待方式的特点:

等待方式是否阻塞线程异常处理适用场景
allOf + join抛出CompletionException,无需声明受检异常需要阻塞等待,且不想处理受检异常的场景
allOf + get抛出InterruptedException和ExecutionException,需要捕获或声明需要明确处理受检异常的场景
allOf + thenRun可以在回调中处理异常不需要阻塞当前线程,希望任务完成后自动执行后续逻辑的场景

注意事项

  • 不要直接循环调用单个CompletableFuture的join或者get方法等待所有任务,这样无法保证等待的效率,而且如果前面的任务执行时间长,会浪费等待时间。
  • 如果不需要获取任务的返回结果,只需要等待所有任务完成,直接使用allOf返回的CompletableFuture即可,不需要再单独获取每个任务的结果。
  • 使用自定义线程池时,等待完成后记得关闭线程池,避免资源泄漏。
  • 如果任务数量不固定,可以将所有CompletableFuture放到集合中,再转换为数组传入allOf方法。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DynamicTaskDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        List<CompletableFuture<String>> taskList = new ArrayList<>();
        // 动态添加任务
        for (int i = 0; i < 5; i++) {
            int index = i;
            CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(200 * (index + 1));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "任务" + index + "完成";
            }, executor);
            taskList.add(task);
        }
        // 转换为数组传入allOf
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(taskList.toArray(new CompletableFuture[0]));
        allTasks.join();
        // 遍历获取结果
        for (CompletableFuture<String> task : taskList) {
            System.out.println(task.join());
        }
        executor.shutdown();
    }
}

CompletableFuture异步任务allOfjoinget修改时间:2026-07-11 22:00:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。