当前位置: 首页 > 面试题库 >

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

东博瀚
2023-03-14
问题内容

我有一个方法可以执行一些超时任务。我使用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)。我认为添加超时功能不应更改我的方法签名以抛出通用的Exception类型。


问题答案:

我已经深入研究了这个问题,很混乱。在Java
5、6或7中没有简单的答案。除了您指出的笨拙,冗长和脆弱之外,您的解决方案实际上还存在一个问题,ExecutionException即您在调用时要剥离的代码getCause()实际上包含了大多数内容。重要的堆栈跟踪信息!

也就是说,在您提供的代码中执行该方法的线程的所有堆栈信息仅在ExcecutionException中,而不在嵌套原因中,而嵌套原因仅覆盖call()Callable中的帧。也就是说,您的doSomethingWithTimeout方法甚至不会出现在您抛出的异常的堆栈跟踪中!您只会从执行程序中获得无实体的堆栈。这是因为ExecutionException是唯一在调用线程上创建的(请参阅FutureTask.get())。

我知道的唯一解决方案很复杂。很多问题起源与自由派异常规范的Callable- throws Exception。您可以定义新的变体,Callable其中的新变体可以精确地指定它们引发的异常,例如:

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

    @Override
    T call() throws X; 
}

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

现在,您可以围绕JDK编写包装,该包装Executor采用增强的Callable并返回增强的功能Future,例如番石榴的CheckedFuture。被检查的异常类型在编译时从的创建和类型传播ExecutorService到返回的Futures,并最终getChecked在将来使用该方法。

这就是您通过编译时类型安全性的方式。这意味着,而不是调用:

Future.get() throws InterruptedException, ExecutionException;

您可以致电:

CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException

因此避免了展开问题-您的方法立即引发所需类型的异常,并且这些异常在编译时可用并进行了检查。

在内部getChecked,但是您 仍然
需要解决上述“遗漏原因”展开问题。您可以通过将(调用线程的)当前堆栈缝合到引发的异常的堆栈上来实现此目的。这扩展了Java中堆栈跟踪的通常用法,因为单个堆栈跨线程扩展,但是一旦知道发生了什么,它就会起作用并且很容易理解。

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



 类似资料:
  • 我有一个超时执行任务的方法。我使用ExecutorServer.submit()获取一个Future对象,然后调用future.get()并超时。这很好,但是我的问题是处理我的任务可能抛出的检查异常的最好方法。下面的代码工作正常,并且保留了被检查的异常,但是如果方法签名中被检查的异常的列表改变了,它看起来非常笨拙并且容易出错。 关于如何解决这个问题的任何建议?我需要以Java 5为目标,但我也很好

  • 因此,我有一个Javascript脚本,它在一个循环中将小的分数相加,它有可能将0.2加到0.1。然后,这个值被输入到另一个函数,但问题是,我需要0.3来精确输入,而不是0.3000000000000004。 什么是最简单的方法,以确保数字是正确和准确的。注意,它可能得到0.25+0.125等,被添加到简单的四舍五入到小数点1不会解决问题。 也有可能添加0.2+0.1000000000000000

  • 问题内容: 我经常遇到以下情况,其中我需要提供许多不同类型的权限。我主要在SQL Server 2000中使用ASP.NET/VB.NET。 设想 我想提供一个可以在不同参数上工作的动态权限系统。假设我想授予部门或特定人员访问应用程序的权限。并假装我们拥有不断增长的应用程序。 过去,我选择了我知道的以下两种方法之一。 将单个许可权表与特殊列一起使用,这些特殊列用于确定如何应用参数。此示例中的特殊列

  • 上周,我决定尝试Perl6,并开始重新实现我的一个程序。我不得不说,Perl6对于对象编程来说非常容易,这对我来说是Perl5非常痛苦的一个方面。 我的程序必须读取和存储大文件,如整个基因组(高达3 Gb或更多,见下面的例子1)或制表数据。 代码的第一个版本是通过逐行迭代以Perl5的方式制作的(“基因组.fa”。对于正确的执行时间,它非常慢且不可行。 所以经过一点RTFM之后,我改变了文件上的s

  • 问题内容: 每次我需要使用Java中的日期和/或时标时,我总是觉得自己做错了事,花了无尽的时间试图找到使用API​​的更好方法,而不必编写自己的Date and Time实用程序类。这是我刚遇到的一些烦人的事情: 从0开始的月份。我意识到最佳实践是使用Calendar.SEPTEMBER而不是8,但是令人讨厌的是8代表9月而不是8月。 获取没有时间戳的日期。我一直需要将日期的时间戳部分清零的实用程

  • 问题内容: 我有一个针对客户的减速器,另一个针对AppToolbar的减速器,还有一些其他的减速器… 现在让我们说我创建了一个删除客户端的提取操作,如果失败,我在Clients reducer中会有代码,该代码应该做一些事情,但是我也想在AppToolbar中显示一些全局错误。 但是客户端和AppToolbar减速器不共享状态的同一部分,因此我无法在减速器中创建新的动作。 那么我应该如何显示全局错