首页 > 解决方案 > 从 SwingWorker 调用 done 方法时是否必须使用 Runnable.run

问题描述

作为学习 SwingWorker 的一部分,我正在浏览源代码,并在其中找到了以下代码。我有一个关于这个的问题。

private void doneEDT() {
        Runnable doDone =
            new Runnable() {
                public void run() {
                    done();
                }
            };
        if (SwingUtilities.isEventDispatchThread()) {
            doDone.run();
        } else {
            doSubmit.add(doDone);
        }
    }

上面的 doneEDT 是从 SwingWorker 构造函数调用的,如下所示,

public SwingWorker() {
        Callable<T> callable =
                new Callable<T>() {
                    public T call() throws Exception {
                        setState(StateValue.STARTED);
                        return doInBackground();
                    }
                };

        future = new FutureTask<T>(callable) {
                       @Override
                       protected void done() {
                           doneEDT();
                           setState(StateValue.DONE);
                       }
                   };

       state = StateValue.PENDING;
       propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
       doProcess = null;
       doNotifyProgressChange = null;
    }

问题

为什么该done()方法包含Runnable在 doneEDT 方法中?done()可以直接调用该方法,而无需将其包装在Runnableright?中。所以我在想将它包装在一个Runnable?

所以这个问题出现在我的脑海里。

标签: javaswingfutureswingworker

解决方案


你把两种不同的done()方法混为一谈。一个是SwingWorker你打算覆盖的。Runnable在您的实现中不需要发生什么特别的事情done(),只有您自己的代码。(一个健壮的实现done()将检查,如果您没有在其他地方调用它isCancelled,可能get()会调用它并处理可能由工作人员抛出的任何异常等。但您不需要包装 aRunnable来执行任何操作。)

另一种方法是FutureTask's。这会让人感到困惑,因为 SwingWorker 本身使用 FutureTask 来完成处理并发的“真正”工作。FutureTask#done()也意味着被客户端覆盖,但在这种情况下,客户端是 SwingWorker,而不是

当您调用 execute() 时,SwingWorker future(FutureTask 的私有实例变量)开始运行,会发生以下情况:

  • future.run()在工作线程中运行。

  • future.run()调用上面引用的 Callable ,将状态更改为 STARTED (发送属性事件)并调用doInBackground. 您自己的工作代码运行并返回结果。

  • future.run()调用它自己的“set”方法来存储结果并将计算标记为完成。它还调用私有方法finishCompletion

  • future.finishCompletion()唤醒任何其他调用 SwingWorker 的线程get()(因此一直被阻塞)。这些调用get()可以在此列表的其余部分发生的同时将计算结果返回给它们的调用者。

  • future.finishCompletion()调用它自己的done(). 通常这是一个空方法,但正如您在上面引用的那样,SwingWorker 进行了覆盖。所以这个特定的FutureTaskdone()做了两件事:首先,它调用SwingWorker#doneEDT(),它只是调用SwingWorker#done()事件调度线程。这是done()您在 SwingWorker 的子类中覆盖的版本。其次,它将状态更改为 DONE,这会发送另一个属性更改事件。

  • future'finishCompletion()返回到它的 'set' 方法,该方法返回到run(),它会做一些家务,然后返回。那时,工作线程被释放用于其他事情(新工作、睡眠、爱好等)。

done()如果你想继承它,你自己的参与只会与 SwingWorker's 相关联。多亏了 SwingWorker 的杂耍,你自己的重写done()保证在事件调度线程上运行,所以你可以安全地使用 Swing 可视化组件做事。请注意,done()如果 SwingWorker 被取消,也会被调用,即使cancel()之前被调用execute()——事实上,done()cancel()返回之前被调用——所以在你的覆盖中相应地检查状态。


推荐阅读