处理ExecutionException的最佳方法是什么? [英] What is the best way to handle an ExecutionException?

查看:230
本文介绍了处理ExecutionException的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a method that performs some task with a timeout. I use the ExecutorServer.submit() to get a Future object, and then I call future.get() with a timeout. This is working fine, but my question is the best way to handle checked exceptions that can be thrown by my task. The following code works, and preserves the checked exceptions, but it seems extremely clumsy and prone to break if the list of checked exceptions in the method signature changes.

有关任何建议怎么解决这个问题?我需要针对Java 5,但我也很想知道Java的新版本是否有很好的解决方案。

Any suggestions on how to fix this? I need to target Java 5, but I'd also be curious to know if there are good solutions in newer versions of 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 );
        }
    }
}

=== UPDATE == =

=== UPDATE ===

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

Many people posted responses that recommended either 1) re-throwing as a general exception, or 2) re-throw as an unchecked exception. I don't want to do either of these, because these exception types (ProcessExecutionException, InterruptedException, IOException, TimeoutException) are important - they will each be handled differently by the calling processed. If I were not needing a timeout feature, then I would want my method to throw these 4 specific exception types (well, except for TimeoutException). I don't think that adding a timeout feature should change my method signature to throw a generic Exception type.

推荐答案

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

I've looked at this problem in depth, and it's a mess. There is no easy answer in Java 5, nor in 6 or 7. In addition to the clumsiness, verbosity and fragility that you point out, your solution actually has the problem that the ExecutionException that you are stripping off when you call getCause() actually contains most of the important stack trace information!

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

That is, all the stack information of the thread executing the method in the code you presented is only in the ExcecutionException, and not in the nested causes, which only cover frames starting at call() in the Callable. That is, your doSomethingWithTimeout method won't even appear in the stack traces of the exceptions you are throwing here! You'll only get the disembodied stack from the executor. This is because the ExecutionException is the only one that was created on the calling thread (see FutureTask.get()).

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

The only solution I know is complicated. A lot of the problem originates with the liberal exception specification of Callable - throws Exception. You can define new variants of Callable which specify exactly which exceptions they throw, such as:

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

    @Override
    T call() throws X; 
}

这允许执行callables的方法具有更精确的抛出子句。如果你想支持最多N个例外的签名,不幸的是你需要这个界面的N个变种。

This allows methods which executes callables to have a more precise throws clause. If you want to support signatures with up to N exceptions, you'll need N variants of this interface, unfortunately.

现在你可以编写一个围绕JDK的包装器 Executor 采用增强的Callable,并返回增强的 Future ,类似guava的 CheckedFuture 。已检查的异常类型在编译时从 ExecutorService 的创建和类型传播到返回的 Future s,并在未来结束 getChecked 方法。

Now you can write a wrapper around the JDK Executor which takes the enhanced Callable, and returns an enhanced Future, something like guava's CheckedFuture. The checked exception type(s) are propagated at compile time from the creation and type of the ExecutorService, to the returned Futures, and end up on the getChecked method on the future.

这就是你编译时的方法通过安全类型。这意味着不是调用:

That's how you thread the compile-time type safety through. This means that rather than calling:

Future.get() throws InterruptedException, ExecutionException;

您可以致电:

CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException

所以解包问题是避免的 - 你的方法会立即抛出所需类型的异常,并且它们在编译时可用并检查。

So the unwrapping problem is avoided - your method immediately throws the exceptions of the required type and they are available and checked at compile time.

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

Inside getChecked, however you still need to solve the "missing cause" unwrapping problem described above. You can do this by stitching the current stack (of the calling thread) onto the stack of the thrown exception. This a stretching the usual use of a stack trace in Java, since a single stack stretches across threads, but it works and is easy to understand once you know what is going on.

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

Another option is to create another exception of the same thing as the one being thrown, and set the original as the cause of the new one. You'll get the full stack trace, and the cause relationship will be the same as how it works with ExecutionException - but you'll have the right type of exception. You'll need to use reflection, however, and is not guaranteed to work, e.g., for objects with no constructor having the usual parameters.

这篇关于处理ExecutionException的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆