SwingWorker:取消工作线程后调用 done() 时抛出错误

SwingWorker: error thrown when done() is called after worker thread is cancelled

我在取消 SwingWorke 任务后看到以下异常。

Exception in thread "AWT-EventQueue-0" java.util.concurrent.CancellationException

网上搜索后发现below comment from Oracle

Note: If get is invoked on a SwingWorker object after its background task has been cancelled, java.util.concurrent.CancellationException is thrown.

这是我生成异常的代码。

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class JasperReportGenerator {

    private JButton cancel;
    private JProgressBar pbar;
    private int count = 0;
    private JFrame dialog;

    public JasperReportGenerator() {
        init();
        //Here we do the background task
        SwingWorker<Boolean, Integer> worker = new SwingWorker<Boolean, Integer>() {
            @Override
            protected Boolean doInBackground() throws Exception {
                for (int i = 0; i < 10; i++) {
                    publish(i * 10);
                    Thread.sleep(1000);
                }
                return true;
            }

            @Override
            protected void done() {
                try {
                    dialog.dispose();
                    JOptionPane.showMessageDialog(null, "Done", get().toString(), JOptionPane.INFORMATION_MESSAGE);
                } catch (InterruptedException ex) {
                    Logger.getLogger(JasperReportGenerator.class.getName()).log(Level.SEVERE, null, ex);
                } catch (ExecutionException ex) {
                    Logger.getLogger(JasperReportGenerator.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            @Override
            protected void process(List<Integer> list) {
                pbar.setValue(list.get(list.size() - 1));
                pbar.setString(list.get(list.size() - 1) + "%");
            }
        };
        worker.execute();

        //cancel the process
        cancel.addActionListener((ActionEvent e) -> {
            worker.cancel(true);
            dialog.dispose();
            System.out.println("bg task cancelled...");
        });

    }

    private void init() {
        dialog = new JFrame("Progress Report");
        dialog.setLocationRelativeTo(null);
        cancel = new JButton("Cancel");
        pbar = new JProgressBar(0, 100);
        pbar.setStringPainted(true);
        pbar.setIndeterminate(false);
        dialog.setPreferredSize(new Dimension(400, 250));
        dialog.setLayout(new FlowLayout());
        dialog.getContentPane().add(pbar);
        dialog.getContentPane().add(cancel);
        dialog.pack();
        dialog.setVisible(true);
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(() -> {
            new JasperReportGenerator();
        });
    }

}

我搜索了一个解决方案,我看到了 this and this 但我无法 fit 我的情况。

这是我done()解决问题的方法

  @Override
        protected void done() {
            try {
                if (!isCancelled()) {
                    JOptionPane.showMessageDialog(null, "Done", get().toString(), JOptionPane.INFORMATION_MESSAGE);
                    dialog.dispose();
                }

            } catch (InterruptedException ex) {
                Logger.getLogger(JasperReportGenerator.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ExecutionException ex) {
                Logger.getLogger(JasperReportGenerator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

我的done()如上设计,异常没有抛出

现在我想知道这是否是nice解决方案?此代码的含义是什么?

您的解决方案不完善,因为取消可能发生在您调用 isCancelled() 和调用 get() 之间,这很少会导致异常。更好的方法是捕获 CancellationException 并处理它。