SwingWorker:究竟什么时候被称为完成方法?

2022-09-02 03:51:21

Javadoc的方法done()SwingWorker:

在 doInBackground 方法完成后在事件调度线程上执行。

我有线索表明,在取消工人的情况下,这不是真的。
在每种情况下(正常终止或取消)都调用,但当它未排队到 EDT 时调用,就像在正常终止时发生的那样。Donecancelled

在取消 a 的情况下,是否有一些更精确的分析来说明何时调用?doneSwingWorker

澄清:这个问题不是关于如何.这里假设 以正确的方式取消了 。
这不是关于线程在它们应该完成时仍然工作。cancelSwingWorkerSwingWorker


答案 1

当线程通过

myWorkerThread.cancel(true/false);

done方法(非常令人惊讶)由 cancel 方法本身调用。

你可能期望发生的事情,但实际上并没有:
- 你调用取消(无论是否使用mayInterrupt)
- 取消设置线程取消
- doInBackground退出
- 完成被调用*
(*完成的排队到EDT,这意味着,如果EDT繁忙,它会在EDT完成它正在做的事情之后发生)

实际发生的情况:
- 您调用 cancel(无论是否使用 mayInterrupt)
- 取消设置线程取消
- done 作为 cancel 代码的一部分调用*
- doInBackground 将在完成循环时退出
(*done 不会排队到 EDT,而是调用取消调用,因此它对 EDT 有非常直接的影响, 这通常是GUI)

我举了一个简单的例子来证明这一点。
复制、粘贴和运行。
1. 我在里面生成一个运行时异常。堆栈线程显示 done 由 cancel 调用。
2.取消后大约4秒后,您将从doInBackground收到一个问候,该问候语强烈地证明在线程退出之前调用了完成。

import java.awt.EventQueue;
import javax.swing.SwingWorker;

public class SwingWorker05 {
public static void main(String [] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
            W w = new W();
            w.execute();
            Thread.sleep(1000);
            try{w.cancel(false);}catch (RuntimeException rte) {
                rte.printStackTrace();
            }
            Thread.sleep(6000);
            } catch (InterruptedException ignored_in_testing) {}
        }

    });
}

public static class W extends SwingWorker <Void, Void> {

    @Override
    protected Void doInBackground() throws Exception {
        while (!isCancelled()) {
            Thread.sleep(5000);
        }
        System.out.println("I'm still alive");
        return null;
    }

    @Override
    protected void done() {throw new RuntimeException("I want to produce a stack trace!");}

}

}

答案 2

done()在任何情况下调用,只要工人被取消或正常完成。然而,在某些情况下,仍在运行并且已经调用了该方法(无论线程是否已经完成,这都是在内部完成的)。一个简单的例子可以在这里找到:doInBackgrounddonecancel()

public static void main(String[] args) throws AWTException {
    SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {

        protected Void doInBackground() throws Exception {
            System.out.println("start");
            Thread.sleep(2000);
            System.out.println("end");
            return null;
        }

        protected void done() {
            System.out.println("done " + isCancelled());
        }
    };
    sw.execute();
    try {
        Thread.sleep(1000);
        sw.cancel(false);
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }

因此,它可以是在完成之前调用的情况。donedoInBackground


推荐