23

我在 Java 6 中使用 SwingWorker 以避免在事件调度线程上运行长时间运行的代码。

如果在我的 done() 方法中调用 get() 返回异常,那么处理异常的适当方法是什么?

我特别担心可能的 InterruptedExceptions。JavaDoc 示例只是忽略了异常,但多年来我了解到吞下异常会导致难以调试的代码。

示例用法如下:

new SwingWorker<String, Void>() {

    @Override
    protected String doInBackground() throws Exception {
        // do long-running calculation
        return result;
    }

    @Override
    protected void done() {
        try {
            setTextField(get());
        } catch (InterruptedException e) {
            e.printStackTrace();  
        } catch (ExecutionException e) {
            e.printStackTrace();  
        }
    }
}.execute();
4

7 回答 7

12

这是一个旧帖子,但我想澄清一下:

SwingWorker.get 抛出 InterruptedException、ExecutionException 作为检查异常。

此外,它还会引发一个非常具体的未经检查的异常,即 CancellationException。当然,它可能会抛出任何未经检查的异常,但 CancellationException 不是“异常”和意外的异常。当您在调用取消之后尝试调用 get 方法时会抛出它。

当在 doInBackground 中引发异常时,将引发 ExecutedException。原始异常包含在 ExecutionException 中。当调用 get() 方法时,将抛出 ExecutionException。取出原始异常并进行管理的想法很好。(正如 Emil H 指出的那样)。

CancellationException 未选中,我认为应该选中。API 实现不检查它的唯一借口是它有一个状态方法 isCancelled()。
您可以:
- 测试 isCancelled() 并且如果为真,则不要调用 get(),因为它会抛出 CancellationException
- 用 try-catch 包围 get() 并添加 CancellationException,因为编译器不会请求未选中
- 事实上CancellationException 未被选中,让您可以忘掉所有这些东西并获得惊喜。
- 做任何事情,因为你不会取消工人

中断异常。如果使用 cancel(true) 取消 SwingThread,doInBackground 中的第一个可中断方法调用(肯定是 Thread.sleep、this.wait,可能还有一些 IO 方法)将抛出 InterruptException。但是这个异常没有包含在 ExecuteException 中。doInBackground 将因异常中断而终止。如果它被捕获并转换为其他异常,则这些异常将被忽略,因为此时 cancel 已经在 EDT 上调用了 SwingThread.done,如果 done 调用了 get,它只得到标准的 CancellationException。不是中断异常!

如果使用 cancel(false) 取消,则不会在 doInBackground 中引发 InterruptException。如果您使用 cancel(true) 取消但在 doInBackground 中没有可中断的方法调用,则相同。在这些情况下,doInBackground 将遵循其自然循环。此循环应测试 isCancelled 方法并正常退出。如果 doInBackground 不这样做,它将永远运行。我没有测试过是否存在超时,但我不这么认为。

对我来说,它仍然只是一个灰色地带。哪些情况下get会抛出InterruptedException?我想看一些简短的代码,因为我无法产生类似的异常。:-)

PS 我在另一个 Question&Answer 中记录了在 doInBackground 退出之前调用完成和状态更改侦听器以防取消。既然这是真的,那么在设计 doInBackground 方法时,这不是一个错误,需要特别注意。如果您对此感兴趣,请参阅SwingWorker:何时调用 done 方法?

于 2011-06-03T18:44:07.530 回答
3

这在很大程度上取决于后台作业可能导致的错误类型。如果 doInBackground 中的作业抛出异常,它将作为嵌套的 ExecutionException 传播到 done 方法。在这种情况下,最佳实践是处理嵌套异常,而不是 ExecutionException 本身。

例如:如果工作线程抛出异常,表明数据库连接已丢失,您可能希望重新连接并重新启动作业。如果要完成的工作依赖于某种已经被使用的资源,最好为使用提供重试或取消选择。如果抛出的异常对用户没有任何影响,只需记录错误并继续。

据我所知,我相信 InterruptedException 不会成为问题,因为您在 done 方法中调用 get 方法,因为只有在等待后台作业时 get 调用被中断时才会抛出 InterruptedException结束。如果发生类似的意外事件,您可能希望显示错误消息并退出应用程序。

于 2009-04-26T17:51:34.040 回答
2

这既是一个界面问题,也是一个错误处理问题。许多应用程序添加了一些列出正在运行的后台作业的小表格。其中一个异常可能会闪现产生错误的表行,或者做一些破坏性的事情,比如显示警报。这取决于异常的严重程度。我认为您可能必须回答的更难的问题是,我们讨论了多少种可能不同类型的异常,以及它们的相对严重性是什么。

我认为一个简单的妥协可能是为您最严重的错误提供模式警报,以及其他任何事情,只需记录事件直到它a)阻止用户继续进行或b)用户关闭文档/窗口,此时例如,您可以在询问是否要保存任何未保存的缓冲区的同时显示在后台处理任务期间发生的异常列表。

于 2009-04-28T05:07:00.663 回答
2

我建议让错误一直传播到操作开始的地方。

例如,如果用户单击按钮以从数据源获取数据。如果出现问题,无论是凭据错误、网络错误、数据库错误还是其他任何问题,您都不适合在该工作线程中设置逻辑来尝试在那里解决它。

但是如果你让它传播到任务开始的地方,你可以从那里进行适当的错误更正,例如再次弹出凭据对话框,显示“重试”对话框,甚至显示错误消息。

于 2009-05-02T05:58:24.250 回答
0

假设这是一个 GUI 应用程序,您可能希望在异常时提供有关失败操作的视觉反馈。

于 2009-04-26T17:20:18.610 回答
0

我应该澄清我原来的帖子。不要简单地在 done() 方法中捕获某些异常类型。在 doInBackground() 中执行的任何代码都可能引发任何类型的异常,并且仅在您的问题中捕获异常可能会导致在 EDT(事件调度线程或主 GUI 线程)上引发异常。在 done() 方法中捕获所有异常类型只是使用 SwingWorkers 时的一个好习惯。

@Override
protected void done()
{
    try
    {
        if(!super.isCancelled())
        {
            super.get();
        }
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
}
于 2017-04-22T16:31:16.730 回答
-2

我猜你不会用 C# 得到很多这样的问题。您需要了解异常是什么并适当地处理它(这通常是让它在堆栈中更进一步)。

InterruptedExceptionThread.interrupt- 当线程在等待(大致)时被(被)中断时抛出。为什么要中断线程?通常你希望线程停止它正在做的事情——重置中断状态并退出。例如,如果小程序线程在一个小程序应该消失后继续运行更长的时间,插件将中断小程序线程。但是,在这种情况下,provideddone被正确调用,您根本不应该等待。因此,将异常包装在一个中是合适的IllegalStateException(API 文档可能应该说明这一点)。这是一个非常糟糕的 API。/模式可能更有意义publishprocess

ExecutionException- 您需要处理包装的异常。如果您不希望出现特定类型的异常,请将其包装在未经检查的异常中。

一般来说,我会建议明确区分 EDT 上发生的事情和 EDT 外发生的事情。因此,避免SwingWorker在生产代码中。

于 2009-04-26T19:07:09.883 回答