CompletableFuture 异常行为与 join() 然后 get()
CompletableFuture exception behavior with join() then get()
我的直觉是下面的代码是错误的。我相信因为正在使用 join() ,所以在完成期货时抛出的任何异常都不会被检查。那么在调用get()时,将不会出现checked exceptions,不会记录任何错误,并且在失败时很难诊断错误。
List<CompletableFuture> list = ImmutableList.of(future1, future2);
CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()])).join();
try {
result1 = future1.get();
result2 = future2.get();
} catch (InterruptedException | ExecutionException e) {
// will this ever run if join() is already called?
}
我已经查看了 CompletableFuture 的文档,但没有找到我的问题的确切答案。我在这里问,然后去阅读源代码。
我能看到 catch 块代码 运行 的唯一原因是,是否可以以某种方式将已检查的异常保存在某些执行上下文中,而不是在 join() 中抛出(或被未检查的异常包裹抛出) ,然后在 get() 之后以某种形式再次抛出。这对我来说似乎不太可能。
所以我的最终问题是,catch 块代码会 运行 吗?
是的,永远无法到达代码,但这并不能使 "code wrong"。
首先,让我们试一试...
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
try
{
CompletableFuture.allOf(future1).join();
}
catch (Exception e1)
{
System.out.println("I'd exit here."); // *1
}
try
{
future1.get();
}
catch (InterruptedException | ExecutionException e)
{
System.out.println("Entered!");
}
由于您没有执行 try/catch
"*1",异常将导致方法退出并且永远不会到达 get()
;所以第二个 catch
子句永远不会被执行。
但是,catch
仍然是必需的,因为它是给编译器的,编译器无法知道之前的调用顺序。
无论如何,更直接的方法是这样的:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
try
{
CompletableFuture.allOf(future1).join();
future1.get();
}
catch (CompletionException e1) // this is unchecked, of course
{
System.out.println("Exception when joining");
}
catch (InterruptedException | ExecutionException e)
{
System.out.println("Exception when getting");
}
join
和get
方法都是阻塞方法,依赖于完成信号和returns结果T
。处理有问题的代码:-
一方面,InterruptedException
在线程等待的过程中可能会抛出get
,这里的等待已经被join
完成了方法。
此外,如 join
方法文档中所述
/**
* ... if a
* computation involved in the completion of this
* CompletableFuture threw an exception, this method throws an
* (unchecked) {@link CompletionException} with the underlying
* exception as its cause.
*/
因此,另一方面,您的案例中 futureN.get()
的 ExecutionException
只能在未来异常完成时抛出。由于未来如果异常执行将最终为 join
调用抛出 CompletionException
,因此它永远不会到达 catch 块或因此 try
块。
我的直觉是下面的代码是错误的。我相信因为正在使用 join() ,所以在完成期货时抛出的任何异常都不会被检查。那么在调用get()时,将不会出现checked exceptions,不会记录任何错误,并且在失败时很难诊断错误。
List<CompletableFuture> list = ImmutableList.of(future1, future2);
CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()])).join();
try {
result1 = future1.get();
result2 = future2.get();
} catch (InterruptedException | ExecutionException e) {
// will this ever run if join() is already called?
}
我已经查看了 CompletableFuture 的文档,但没有找到我的问题的确切答案。我在这里问,然后去阅读源代码。
我能看到 catch 块代码 运行 的唯一原因是,是否可以以某种方式将已检查的异常保存在某些执行上下文中,而不是在 join() 中抛出(或被未检查的异常包裹抛出) ,然后在 get() 之后以某种形式再次抛出。这对我来说似乎不太可能。
所以我的最终问题是,catch 块代码会 运行 吗?
是的,永远无法到达代码,但这并不能使 "code wrong"。
首先,让我们试一试...
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
try
{
CompletableFuture.allOf(future1).join();
}
catch (Exception e1)
{
System.out.println("I'd exit here."); // *1
}
try
{
future1.get();
}
catch (InterruptedException | ExecutionException e)
{
System.out.println("Entered!");
}
由于您没有执行 try/catch
"*1",异常将导致方法退出并且永远不会到达 get()
;所以第二个 catch
子句永远不会被执行。
但是,catch
仍然是必需的,因为它是给编译器的,编译器无法知道之前的调用顺序。
无论如何,更直接的方法是这样的:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
try
{
CompletableFuture.allOf(future1).join();
future1.get();
}
catch (CompletionException e1) // this is unchecked, of course
{
System.out.println("Exception when joining");
}
catch (InterruptedException | ExecutionException e)
{
System.out.println("Exception when getting");
}
join
和get
方法都是阻塞方法,依赖于完成信号和returns结果T
。处理有问题的代码:-
一方面,InterruptedException
在线程等待的过程中可能会抛出get
,这里的等待已经被join
完成了方法。
此外,如 join
方法文档中所述
/**
* ... if a
* computation involved in the completion of this
* CompletableFuture threw an exception, this method throws an
* (unchecked) {@link CompletionException} with the underlying
* exception as its cause.
*/
因此,另一方面,您的案例中 futureN.get()
的 ExecutionException
只能在未来异常完成时抛出。由于未来如果异常执行将最终为 join
调用抛出 CompletionException
,因此它永远不会到达 catch 块或因此 try
块。