【问题标题】:Is it mandatory to use Runnable.run while calling done method from SwingWorker从 SwingWorker 调用 done 方法时是否必须使用 Runnable.run
【发布时间】:2020-02-26 02:48:46
【问题描述】:

作为学习 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() 方法被包装在 doneEDT 方法中的Runnable 中? done() 方法可以直接调用,而无需将其包装在 Runnable 中,对吗?所以我在想把它包装在Runnable 里有什么好处?

于是我想到了这个问题。

【问题讨论】:

  • 它似乎没有任何意义,因为它直接调用run。 (通常,Runnable 会被传递给其他一些上下文来执行,例如 invokeLater。)
  • 但是 done() 方法可以直接从 doneEDT() 方法中调用,对吧?这也说得通吧?
  • 我错过了if;这就是区别。它可以将运行发送到线程外。 (在现代 Java 中,this::done 会更清晰。)
  • 您所说的“它可以将运行发送到线程外”是什么意思?
  • 检查else中发生的事情。

标签: java swing future swingworker


【解决方案1】:

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

另一种方法是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 进行了覆盖。所以这个特殊的FutureTask done() 做了两件事:首先,它调用SwingWorker#doneEDT(),它只是在事件调度线程上调用SwingWorker#done()。这是您在 SwingWorker 的子类中覆盖的 done() 的版本。其次,它将状态更改为 DONE,这会发送另一个属性更改事件。

  • futurefinishCompletion() 返回到它的“set”方法,该方法返回到run(),它做一些内务处理,然后返回。此时,工作线程被释放用于其他事情(新工作、睡眠、爱好等)。

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

【讨论】:

    猜你喜欢
    • 2011-09-06
    • 1970-01-01
    • 2019-10-09
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 2015-02-07
    • 1970-01-01
    • 2017-11-25
    相关资源
    最近更新 更多