处理 SwingWorker.doInBackground 抛出的异常的正确方法

The proper way to handle exceptions thrown by the SwingWorker.doInBackground

处理 doInBackground method of SwingWorker class is to invoke the get method from within the done method, as explained here and here.

抛出的异常的正确方法

get 方法的文档说明如下:

Waits if necessary for the computation to complete, and then retrieves its result.

Note: calling get on the Event Dispatch Thread blocks all events, including repaints, from being processed until this SwingWorker is complete.

因此,如果 get 方法导致 done 方法内的等待,实际上它会阻塞事件调度线程,因为 done 方法是在 EDT 上执行的。

但是,在对建议的解决方案进行简单测试后,您可能会注意到 EDT 未被阻止:出现此行为是因为在 done 方法中调用了 get 方法,因此get 在计算出运算结果后调用,因此调用它不会阻塞 EDT。这个动机正确吗?

Therefore, if the get method causes a waiting within the done method, in fact it would block the Event Dispatch Thread, since the done method is executed on EDT.

实际上,如果调用 done,doInBackground 已经 returned,因此在 done 中调用 get 不会阻塞事件调度线程。

如果您使用 PropertyChangeListener 支持并监视状态更改为 DONE

,情况也是如此

已更新

所以,在查看了 SwingWorker calls 'done' before the 'doInBackground' is finished 之后,这意味着在取消的 worker 上调用 get 会自动阻止 EDT,基本的解决方法实际上是忽略 return 通过检查 SwingWorker#isCancelled 状态得到结果。让我们面对现实吧,如果工人被取消,return 结果是 unknown/undefined,所以最好不要尝试 get 它。

举个例子(基于 bug 的代码)

import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;

public class Main {

    public static void main(String[] args) throws InterruptedException {
        SwingWorker<String, String> worker = new SwingWorker<String, String>() {
            @Override
            protected String doInBackground() throws Exception {
                try {
                    while (!Thread.currentThread().isInterrupted()) {
                        System.out.println("Working...");
                        Thread.sleep(1000);

                    }
                } catch (InterruptedException ex) {
                    System.out.println("Got interrupted!");
                }

                try {
                    System.out.println("Cleaning up");
                    Thread.sleep(10000);
                    System.out.println("Done cleaning");
                } catch (InterruptedException ex) {
                    System.out.println("Got interrupted second time!");
                }

                return null;
            }

            @Override
            protected void done() {
                System.out.println("Done");
                if (!isCancelled()) {
                    long start = System.currentTimeMillis();
                    try {
                        get();
                    } catch (InterruptedException | ExecutionException ex) {
                        ex.printStackTrace();
                    }
                    long end = System.currentTimeMillis();
                    System.out.println("Took " + ((end - start) / 1000d));
                } else {
                    System.out.println("Was cancelled");
                }
            }
        };

        worker.execute();

        Thread.sleep(10000);

        worker.cancel(true);
        Thread.sleep(20000);
    }
}
@Override
protected void done()
{
    try
    {
        if(!super.isCancelled())
        {
            super.get();
        }
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
}