CompletableFuture 提供了 get() 和 join() 方法,它们都用于等待异步计算的结果,但它们在异常处理和行为上有一些关键的区别。
1. get() 方法
get() 方法是 java.util.concurrent.Future 接口的一部分。它用于等待任务完成并返回结果。如果任务正常完成,则返回结果。如果任务执行时抛出异常,则会将异常封装在 ExecutionException 中并抛出。
特点:
- 阻塞:
get()会阻塞当前线程,直到异步任务完成。 - 异常处理:如果异步任务抛出了异常,
get()会将该异常包装在ExecutionException中并抛出。你需要处理ExecutionException和InterruptedException。
示例:
import java.util.concurrent.*;
public class GetExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);
// 阻塞等待异步任务的结果
Integer result = future.get();
System.out.println("Result: " + result); // 输出: Result: 42
// 如果抛出异常,可以捕获
CompletableFuture<Integer> faultyFuture = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Some error");
});
try {
faultyFuture.get(); // 会抛出 ExecutionException
} catch (ExecutionException e) {
System.out.println("Exception occurred: " + e.getCause()); // 输出: Some error
}
}
}
2. join() 方法
join() 方法是 CompletableFuture 类特有的方法,类似于 get(),也用于等待异步任务完成并返回结果。但是,它与 get() 的区别在于异常处理。
特点:
- 阻塞:
join()也会阻塞当前线程,直到异步任务完成。 - 异常处理:
join()不会抛出ExecutionException,而是直接将异常包装成CompletionException。如果任务执行时抛出异常,join()会将异常封装在CompletionException中并抛出。
示例:
import java.util.concurrent.*;
public class JoinExample {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);
// 阻塞等待异步任务的结果
Integer result = future.join();
System.out.println("Result: " + result); // 输出: Result: 42
// 如果抛出异常,可以捕获
CompletableFuture<Integer> faultyFuture = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Some error");
});
try {
faultyFuture.join(); // 会抛出 CompletionException
} catch (CompletionException e) {
System.out.println("Exception occurred: " + e.getCause()); // 输出: Some error
}
}
}
3. 主要区别总结
| 特性 | get() 方法 | join() 方法 |
|---|---|---|
| 异常包装 | 如果异步任务抛出异常,get() 会抛出 ExecutionException,需要用 getCause() 获取原始异常。 | 如果异步任务抛出异常,join() 会抛出 CompletionException,需要用 getCause() 获取原始异常。 |
| 异常类型 | ExecutionException 和 InterruptedException。 | CompletionException。 |
| 中断 | 如果线程在等待时被中断,get() 会抛出 InterruptedException。 | 如果线程在等待时被中断,join() 会抛出 CompletionException,包含 InterruptedException。 |
| 推荐使用场景 | get() 更适合于需要同时处理多个异常类型的场景,尤其是在与 Future 配合使用时。 | join() 更简洁,适合于不需要关心异常类型的场景,尤其是当你使用 CompletableFuture 时。 |
4. 选择哪个方法
- 如果你需要处理任务执行过程中可能抛出的异常并且希望详细了解异常类型,选择
get()。因为它会明确地将异常包装在ExecutionException中,允许你访问getCause()方法获取底层的异常。 - 如果你不关心异常类型,或者只是想在任务失败时捕获异常,选择
join()。它将异常包装成CompletionException,并且避免了InterruptedException和ExecutionException的复杂处理。
总结:
get()和join()都会阻塞当前线程,直到异步任务完成。get()抛出ExecutionException和InterruptedException,而join()抛出CompletionException。- 一般情况下,
join()更简洁,适用于CompletableFuture,而get()更适合与Future配合使用并需要处理多种类型的异常时。