有什么区别吗? SwingWorker#publish vs SwingUtilities#invokeLater

问题描述

比方说,我们有一个繁重的任务必须在后台运行,并将其进度或任何内容发布到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()方法,而不是在SwingUtilities.invokelater方法末尾调用doInBackground?除了处理可能在doInBackground方法中引发的异常。

解决方法

在类publish()中查看方法SwingWorker javadoc

因为处理方法是在事件调度线程上异步调用的,所以在执行处理方法之前可能会多次调用publish方法。为了提高性能,所有这些调用都被合并为一个带有串联参数的调用。

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

关于类done()中的方法SwingWorker,您也曾问过,再次请您参考 javadoc

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

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