gpt4 book ai didi

java - 处理 ExecutionException 的最佳方法是什么?

转载 作者:IT老高 更新时间:2023-10-28 20:52:10 26 4
gpt4 key购买 nike

我有一个方法可以超时执行某些任务。我使用 ExecutorServer.submit() 来获取 Future 对象,然后我调用 future.get() 并超时。这工作正常,但我的问题是处理我的任务可能引发的已检查异常的最佳方法。以下代码有效,并保留了已检查的异常,但如果方法签名中的已检查异常列表发生更改,它似乎非常笨拙且容易中断。

关于如何解决此问题的任何建议?我需要以 Java 5 为目标,但我也很想知道在较新版本的 Java 中是否有好的解决方案。

public static byte[] doSomethingWithTimeout( int timeout ) throws ProcessExecutionException, InterruptedException, IOException, TimeoutException {

Callable<byte[]> callable = new Callable<byte[]>() {
public byte[] call() throws IOException, InterruptedException, ProcessExecutionException {
//Do some work that could throw one of these exceptions
return null;
}
};

try {
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<byte[]> future = service.submit( callable );
return future.get( timeout, TimeUnit.MILLISECONDS );
} finally {
service.shutdown();
}
} catch( Throwable t ) { //Exception handling of nested exceptions is painfully clumsy in Java
if( t instanceof ExecutionException ) {
t = t.getCause();
}
if( t instanceof ProcessExecutionException ) {
throw (ProcessExecutionException)t;
} else if( t instanceof InterruptedException ) {
throw (InterruptedException)t;
} else if( t instanceof IOException ) {
throw (IOException)t;
} else if( t instanceof TimeoutException ) {
throw (TimeoutException)t;
} else if( t instanceof Error ) {
throw (Error)t;
} else if( t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException( t );
}
}
}

=== 更新 ===

许多人发布的回复建议 1) 作为一般异常重新抛出,或 2) 作为未经检查的异常重新抛出。我不想做这些,因为这些异常类型(ProcessExecutionException、InterruptedException、IOException、TimeoutException)很重要——它们每个都将被处理的调用以不同的方式处理。如果我不需要超时功能,那么我希望我的方法抛出这 4 种特定的异常类型(好吧,TimeoutException 除外)。我不认为添加超时功能应该将我的方法签名更改为抛出通用异常类型。

最佳答案

我已经深入研究过这个问题,结果一团糟。在 Java 5、6 或 7 中都没有简单的答案。除了您指出的笨拙、冗长和脆弱之外,您的解决方案实际上还有一个问题,即您正在剥离的 ExecutionException当你调用 getCause() 时实际上包含了大部分重要的堆栈跟踪信息!

也就是说,你提供的代码中执行方法的线程的所有堆栈信息都只在ExecutionException中,而不在嵌套原因中,它只涵盖从call()开始的帧在可调用。也就是说,您的 doSomethingWithTimeout 方法甚至不会出现在您在这里抛出的异常的堆栈跟踪中!您只会从 executor 那里获得无实体的堆栈。这是因为 ExecutionException 是唯一在调用线程上创建的(参见 FutureTask.get())。

我知道的唯一解决方案很复杂。很多问题都源于 Callable - throws Exception 的自由异常规范。您可以定义 Callable 的新变体,准确指定它们抛出的异常,例如:

public interface Callable1<T,X extends Exception> extends Callable<T> {

@Override
T call() throws X;
}

这允许执行可调用对象的方法具有更精确的 throws 子句。如果您想支持最多包含 N 个异常的签名,那么很遗憾,您将需要此接口(interface)的 N 个变体。

现在您可以在JDK Executor 周围编写一个包装器,它采用增强的Callable,并返回一个增强的Future,类似于guava 的CheckedFuture .检查的异常类型在编译时从 ExecutorService 的创建和类型传播到返回的 Future,并最终在 getChecked 关于 future 的方法。

这就是您如何通过线程来实现编译时类型安全。这意味着,而不是调用:

Future.get() throws InterruptedException, ExecutionException;

您可以调用:

CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException

因此避免了展开问题 - 您的方法会立即抛出所需类型的异常,并且它们在编译时可用和检查。

getChecked 内部,但是您仍然需要解决上述“缺失原因”展开问题。您可以通过将当前堆栈(调用线程的)拼接到抛出异常的堆栈上来做到这一点。这是扩展 Java 中堆栈跟踪的常用用法,因为单个堆栈跨越线程,但是一旦您知道发生了什么,它就可以工作并且很容易理解。

另一种选择是创建 another 与被抛出的相同事物的异常,并将原始异常设置为新异常的原因。您将获得完整的堆栈跟踪,原因关系将与它与 ExecutionException 的工作方式相同 - 但您将拥有正确的异常类型。但是,您需要使用反射,并且不能保证可以正常工作,例如,对于没有具有常用参数的构造函数的对象。

关于java - 处理 ExecutionException 的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10437890/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com