在Java异步编程场景中,我们经常会提交多个CompletableFuture异步任务并行执行,之后需要等待所有任务全部完成再汇总结果或者执行后续逻辑。不同的等待方式在异常处理、阻塞特性、返回值等方面存在差异,需要根据实际需求选择合适的方法。

使用allOf方法结合join等待
CompletableFuture的静态方法allOf可以接收多个CompletableFuture实例,返回一个全新的CompletableFuture<Void>对象,当所有传入的异步任务都完成时,这个新的CompletableFuture才会完成。如果我们需要等待所有任务完成,并且不关心每个任务的单独返回值,可以使用这种方式。
示例代码如下:
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(); // 阻塞等待所有任务完成
System.out.println("所有异步任务都已完成");
// 如果需要获取每个任务的结果,可以单独调用join
try {
String result1 = task1.get();
String result2 = task2.get();
String result3 = task3.get();
System.out.println("任务1结果:" + result1);
System.out.println("任务2结果:" + result2);
System.out.println("任务3结果:" + result3);
} catch (Exception e) {
e.printStackTrace();
}
executor.shutdown();
}
}
这种方式中,join方法会阻塞当前线程直到对应的CompletableFuture完成,和get方法的区别是join不会抛出受检异常,不需要强制捕获,使用起来更简洁。
使用allOf方法结合get等待
如果我们希望捕获等待过程中的异常,或者需要遵循方法签名抛出异常的要求,可以使用get方法来等待allOf返回的任务完成。get方法会抛出InterruptedException和ExecutionException,需要我们进行异常处理。
示例代码如下:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureGetDemo {
public static void main(String[] args) {
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> allTask = CompletableFuture.allOf(taskA, taskB);
try {
allTask.get(); // 阻塞等待,会抛出受检异常
System.out.println("所有任务完成,任务A结果:" + taskA.get() + ",任务B结果:" + taskB.get());
} catch (Exception e) {
System.out.println("等待任务过程中出现异常:" + e.getMessage());
}
executor.shutdown();
}
}
等待过程中异常处理注意事项
当使用allOf等待多个任务时,如果其中任意一个任务出现异常,那么allOf返回的CompletableFuture也会异常完成,此时调用join或者get都会抛出异常。如果需要处理单个任务的异常,避免单个任务失败影响整体等待,需要提前给每个异步任务添加异常处理逻辑。
示例代码如下:
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(3);
// 任务3会抛出异常
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1正常完成", executor);
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2正常完成", executor);
CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("任务3执行失败");
}, executor).exceptionally(ex -> {
// 处理任务3的异常,返回默认值,避免任务3异常导致整体等待失败
System.out.println("任务3出现异常,已处理:" + ex.getMessage());
return "任务3默认结果";
});
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2, task3);
allTasks.join();
System.out.println("所有任务处理完成");
System.out.println("任务1结果:" + task1.join());
System.out.println("任务2结果:" + task2.join());
System.out.println("任务3结果:" + task3.join());
executor.shutdown();
}
}
不同等待方式对比
以下是几种常见等待方式的对比:
| 等待方式 | 阻塞特性 | 异常处理 | 适用场景 |
|---|---|---|---|
| allOf + join | 阻塞当前线程 | 不抛出受检异常,运行时异常直接抛出 | 不需要捕获受检异常,代码简洁的场景 |
| allOf + get | 阻塞当前线程 | 抛出InterruptedException和ExecutionException,需要显式处理 | 需要捕获等待过程异常,或者方法签名要求抛出受检异常的场景 |
| allOf + thenRun回调 | 不阻塞当前线程,任务完成后触发回调 | 回调中处理异常,或者提前给每个任务加异常处理 | 非阻塞等待,等待完成后执行后续逻辑的场景 |
非阻塞等待方式
如果我们不希望阻塞当前线程,可以在allOf返回的任务上添加回调,当所有任务完成时自动执行后续逻辑,不需要主动调用join或者get进行阻塞等待。
示例代码如下:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureCallbackDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "回调任务1完成";
}, executor);
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "回调任务2完成";
}, executor);
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2);
// 添加回调,所有任务完成后执行,不阻塞当前线程
allTasks.thenRun(() -> {
System.out.println("所有任务完成,执行回调逻辑");
System.out.println("任务1结果:" + task1.join());
System.out.println("任务2结果:" + task2.join());
});
System.out.println("主线程继续执行其他逻辑");
executor.shutdown();
}
}
这种非阻塞方式适合异步任务等待后不需要立即获取结果,而是执行一些后续操作的场景,不会阻塞主线程的执行。
CompletableFuture异步任务allOfjoinget修改时间:2026-06-13 12:54:45