当前位置: 首页 > 知识库问答 >
问题:

处理执行异常的最佳方法是什么?

何峰
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)。我不认为添加超时功能应该改变我的方法签名以抛出泛型异常类型。

共有3个答案

沈巴英
2023-03-14

这里有一些关于检查和反对检查异常的有趣信息。Brian Goetz讨论和Eckel讨论中反对检查异常的论点。但我不知道您是否已经实现并考虑了Joshua在本书中讨论的检查异常重构。

根据有效的Java规则,处理检查异常的首选方法之一是将检查异常转化为非检查异常。比如说,

try{
obj.someAction()
}catch(CheckedException excep){
}

将此实现更改为

if(obj.canThisOperationBeperformed){
obj.someAction()
}else{
// Handle the required Exception.
}
公羊子真
2023-03-14

这是我在这种情况下所做的。这实现了以下目标:

    < li >重新引发检查过的异常,而不包装它们 < li >将堆栈轨迹粘合在一起

代码:

public <V> V waitForThingToComplete(Future<V> future) {
    boolean interrupted = false;
    try {
        while (true) {
            try {
                return future.get();
            } catch (InterruptedException e) {
                interrupted = true;
            }
        }
    } catch (ExecutionException e) {
        final Throwable cause = e.getCause();
        this.prependCurrentStackTrace(cause);
        throw this.<RuntimeException>maskException(cause);
    } catch (CancellationException e) {
        throw new RuntimeException("operation was canceled", e);
    } finally {
        if (interrupted)
            Thread.currentThread().interrupt();
    }
}

// Prepend stack frames from the current thread onto exception trace
private void prependCurrentStackTrace(Throwable t) {
    final StackTraceElement[] innerFrames = t.getStackTrace();
    final StackTraceElement[] outerFrames = new Throwable().getStackTrace();
    final StackTraceElement[] frames = new StackTraceElement[innerFrames.length + outerFrames.length];
    System.arraycopy(innerFrames, 0, frames, 0, innerFrames.length);
    frames[innerFrames.length] = new StackTraceElement(this.getClass().getName(),
      "<placeholder>", "Changed Threads", -1);
    for (int i = 1; i < outerFrames.length; i++)
        frames[innerFrames.length + i] = outerFrames[i];
    t.setStackTrace(frames);
}

// Checked exception masker
@SuppressWarnings("unchecked")
private <T extends Throwable> T maskException(Throwable t) throws T {
    throw (T)t;
}

似乎有用。

沃侯林
2023-03-14

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

也就是说,在你提供的代码执行方法的线程的所有堆栈信息仅在ExcecutionException中,而不是在嵌套原因中,嵌套原因仅涵盖从Callable中的call()开始的帧。也就是说,您的 doSomethingWithTimeout 方法甚至不会出现在您在此处引发的异常的堆栈跟踪中!您只能从执行器获得无形堆栈。这是因为执行异常是唯一在调用线程上创建的(请参阅 FutureTask.get())。

我知道的唯一解决方案很复杂。许多问题源于< code > Callable -< code > throws Exception 的自由异常规范。您可以定义< code>Callable的新变体,这些变体确切地指定它们将引发哪些异常,例如:

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

    @Override
    T call() throws X; 
}

这允许执行可调用函数的方法具有更精确的<code>抛出</code>子句。不幸的是,如果您想支持最多有N个异常的签名,您将需要该接口的N个变体。

现在,您可以围绕JDK<code>执行器,类似于guava的CheckedFuture。已检查的异常类型在编译时从ExecutorService的创建和类型传播到返回的未来s,并最终在未来的getChecked方法上。

这就是编译时类型安全的实现方式。这意味着与其调用:

Future.get() throws InterruptedException, ExecutionException;

您可以拨打:

CheckedFuture.getChecked() throws InterruptedException, ProcessExecutionException, IOException

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

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

另一种选择是创建另一个与被抛出的异常相同的异常,并将原始异常设置为新异常的原因。您将获得完整的堆栈跟踪,原因关系将与< code>ExecutionException的工作方式相同,但您将获得正确的异常类型。然而,你需要使用反射,但这并不保证有效,例如,对于没有普通参数的构造函数的对象。

 类似资料:
  • 我有一个瞬移工作,接受Kafka的主题,通过一堆操作员。我想知道什么是最好的方法来处理中间发生的异常。 假设存在异常,使用并在catch块中输出到,并在调用外部服务以更新另一个相关作业状态的末尾为提供单独的接收器函数 但是,我的问题是,通过这样做,我似乎仍然需要调用并传入一个空值,以便继续到下面的运算符并进入最后一个阶段,在这个阶段,将流入单独的接收器函数。这样做对吗? 另外,我不确定如果不在操作

  • 问题内容: 我有一个方法可以执行一些超时任务。我使用ExecutorServer.submit()获取Future对象,然后使用超时调用future.get()。这工作正常,但是我的问题是处理可能由我的任务引发的检查异常的最佳方法。以下代码可以正常工作,并保留检查的异常,但是如果方法签名中的检查的异常列表发生更改,则显得非常笨拙且容易中断。 对于如何解决这个问题,有任何的建议吗?我需要以Java

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

  • 问题内容: 我正在尝试编写一个小型节点应用程序,该应用程序将搜索并解析文件系统上的大量文件。为了加快搜索速度,我们尝试使用某种类型的map reduce。该计划将是以下简化方案: Web请求带有搜索查询 启动3个进程,每个进程分配1000个(不同)文件 进程完成后,它将“返回”结果回到主线程 一旦所有进程完成,主线程将通过返回组合结果作为JSON结果来继续 我对此有以下疑问: 这在Node中可行吗

  • 问题内容: 是否有适用于javascript的良好分析器?我知道firebug对分析代码提供了一些支持。但是我想确定更大范围的统计数据。想象一下,您正在构建大量的javascript代码,并且您想确定代码中实际上是什么瓶颈。首先,我想查看每个javascript函数和执行时间的配置文件统计信息。接下来将包括DOM函数。这与放慢速度的操作(如对渲染树的操作)相结合将是完美的。我认为,如果在我的代码,

  • 我有一个rest服务,它将抛出一个异常,我想知道什么将是最好的方式来处理这一点。 我想知道这是rest服务中处理异常的正确方式吗? 我在用泽西。