【问题标题】:Is there any difference? SwingWorker#publish vs SwingUtilities#invokeLater有什么区别吗? SwingWorker#publish vs SwingUtilities#invokeLater
【发布时间】:2020-10-22 09:58:49
【问题描述】:

假设我们有一个长期/繁重的任务,必须在后台运行,并将其进度或其他任何内容发布到 GUI。我知道这个发布必须发生在事件调度线程上。这就是我们使用SwingWorker 来完成任务的原因。

所以,我们所做的是这样的:

public class WorkerTest {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new FlowLayout());

            JLabel label = new JLabel();
            frame.add(label);
            startWorker(label);

            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }

    private static void startWorker(JLabel label) {
        new SwingWorker<Integer, Integer>() {

            @Override
            protected Integer doInBackground() throws Exception {
                for (int i = 0; i < 500; i++) {
                    publish(i);
                    Thread.sleep(500); //Simulate long task
                }
                return null;
            }

            @Override
            protected void process(List<Integer> chunks) {
                Integer integer = chunks.get(0);
                label.setText(String.valueOf(integer));
            }
        }.execute();
    }
}

我的问题是,以上内容与此有何不同:

private static void startWorker(JLabel label) {
    new SwingWorker<Integer, Integer>() {

        @Override
        protected Integer doInBackground() throws Exception {
            for (int i = 0; i < 500; i++) {
                int i2 = i;
                SwingUtilities.invokeLater(() -> {
                    label.setText(String.valueOf(i2));
                });
                Thread.sleep(500); //Simulate long task
            }
            return null;
        }

    }.execute();
}

在这两种情况下,作为 GUI 更新的 label.setText() 都会运行到事件调度线程。它们有何不同?

当然,问题还在于为什么我应该对工作人员实现done() 方法而不是在doInBackground 方法的末尾调用SwingUtilities.invokeLater?除了处理doInBackground方法中可能抛出的异常。

【问题讨论】:

  • 为什么要重新发明轮子? process()、done() 方法有助于在逻辑上分解处理。如果你想使用 SwingUtilities.invokeLater() 那么为什么要使用 SwingWorker?只需使用单独的线程。
  • @camickr 这是一个非常有效的问题。我也想知道。我根据@Abra 的回答做了一些测试,发现了区别。但是,如果我使用invokeAndWait 而不是invokeLater,那么这种差异会变得更小。即使性能有小幅下降,事件也会按照我想要的方式以正确的顺序发布。所以,我想在invokeAndWait 中使用Thread,我可以完成SwingWorker 的行为。我说的对吗?
  • 我不知道合并过程是如何工作的。我一直认为调用 process() 方法就像调用 invokeLater() 一样,因为代码被添加到 EDT 进行处理。

标签: java swing swingworker edt


【解决方案1】:

查看类SwingWorker 中方法publish() 的javadoc。

因为进程方法是在事件调度线程上异步调用的,所以在执行进程方法之前可能会多次调用发布方法。出于性能目的,所有这些调用都合并为一个带有串联参数的调用。

根据您问题中的代码,直接从方法 doInBackground() 调用 SwingUtilities.invokeLater() 不会执行合并。或许您能想到为什么需要合并的原因?另请参考Tasks that Have Interim Results

关于SwingWorker 类中的方法done(),你也问过,我再次向你推荐javadoc

doInBackground 方法完成后在事件调度线程上执行。默认实现什么也不做。子类可以覆盖此方法以在事件调度线程上执行完成操作。请注意,您可以在该方法的实现内部查询状态以确定该任务的结果或该任务是否已被取消。

因此您不必重写方法done()。就个人而言,我通常向我的SwingWorker 对象添加一个属性侦听器,用于处理在 [SwingWorker] 任务完成后我需要执行的任务。但当然是 YMMV。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 2012-11-22
    • 2020-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    相关资源
    最近更新 更多