【问题标题】:How cancel the execution of a SwingWorker?如何取消 SwingWorker 的执行?
【发布时间】:2011-09-01 02:52:02
【问题描述】:

目前我有两个 SwingWorker 线程在后台工作。如果发生异常,该方法停止工作,但线程仍在运行。

如果发生异常,如何停止执行并杀死doInBackground() 的线程?

this.cancel(true) 不要销毁/关闭线程。我怎样才能做到这一点?

@Override
protected Boolean doInBackground() throws Exception {
        try {
            while (true) {
                //some code here                   
                return true;
            }
        } catch (Exception e) {       
            this.cancel(true); //<-- this not cancel the thread               
            return false;
        }
    }

我在调试 Netbeans 时看到了这些线程。

'AWT-EventQueue-0' em execução
'AWT-Windows' em execução
'SwingWorker-pool-1-thread-1' em execução
'SwingWorker-pool-1-thread-2' em execução

//*em execução = in execution

【问题讨论】:

  • 你能确认doInBackground()返回并且没有阻塞在while (true)循环内吗?
  • 是的,我调试了代码,它进入了返回false的异常。

标签: java multithreading swing swingworker cancellation


【解决方案1】:

有一个cancel() 方法。您的代码必须注意这一点。如果它在出现异常后继续运行,则表明您的代码忽略了不应出现的异常。

【讨论】:

  • 是的,我看到了类似的东西,但是这些方法在异常内部调用,带有 this.cancel(true);还是在调用 SwingWorker 的 execute 方法的类中?
  • cancel 方法适用于调用执行的对象。如果 SwingWorker 因异常需要停止执行,则需要正确处理该异常。
【解决方案2】:

正如jzd所说,有方法cancel(boolean mayInterruptIfRunning);例如

编辑:取消(真);你必须(总是)发现一个例外java.util.concurrent.CancellationException

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

public class SwingWorkerExample extends JFrame implements ActionListener {

    private static final long serialVersionUID = 1L;
    private final JButton startButton, stopButton;
    private JScrollPane scrollPane = new JScrollPane();
    private JList listBox = null;
    private DefaultListModel listModel = new DefaultListModel();
    private final JProgressBar progressBar;
    private mySwingWorker swingWorker;

    public SwingWorkerExample() {
        super("SwingWorkerExample");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().setLayout(new GridLayout(2, 2));
        startButton = makeButton("Start");
        stopButton = makeButton("Stop");
        stopButton.setEnabled(false);
        progressBar = makeProgressBar(0, 99);
        listBox = new JList(listModel);
        scrollPane.setViewportView(listBox);
        getContentPane().add(scrollPane);
        //Display the window.
        pack();
        setVisible(true);
    }
//Class SwingWorker<T,V> T - the result type returned by this SwingWorker's doInBackground
//and get methods V - the type used for carrying out intermediate results by this SwingWorker's 
//publish and process methods

    private class mySwingWorker extends javax.swing.SwingWorker<ArrayList<Integer>, Integer> {
//The first template argument, in this case, ArrayList<Integer>, is what s returned by doInBackground(), 
//and by get(). The second template argument, in this case, Integer, is what is published with the 
//publish method. It is also the data type which is stored by the java.util.List that is the parameter
//for the process method, which recieves the information published by the publish method.

        @Override
        protected ArrayList<Integer> doInBackground() {
//Returns items of the type given as the first template argument to the SwingWorker class.
            if (javax.swing.SwingUtilities.isEventDispatchThread()) {
                System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() returned true.");
            }
            Integer tmpValue = new Integer(1);
            ArrayList<Integer> list = new ArrayList<Integer>();
            for (int i = 0; i < 100; i++) {
                for (int j = 0; j < 100; j++) { //find every 100th prime, just to make it slower
                    tmpValue = FindNextPrime(tmpValue.intValue());
//isCancelled() returns true if the cancel() method is invoked on this class. That is the proper way
//to stop this thread. See the actionPerformed method.
                    if (isCancelled()) {
                        System.out.println("SwingWorker - isCancelled");
                        return list;
                    }
                }
//Successive calls to publish are coalesced into a java.util.List, which is what is received by process, 
//which in this case, isused to update the JProgressBar. Thus, the values passed to publish range from 
//1 to 100.
                publish(new Integer(i));
                list.add(tmpValue);
            }
            return list;
        }//Note, always use java.util.List here, or it will use the wrong list.

        @Override
        protected void process(java.util.List<Integer> progressList) {
//This method is processing a java.util.List of items given as successive arguments to the publish method.
//Note that these calls are coalesced into a java.util.List. This list holds items of the type given as the
//second template parameter type to SwingWorker. Note that the get method below has nothing to do with the 
//SwingWorker get method; it is the List's get method. This would be a good place to update a progress bar.
            if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
                System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
            }
            Integer percentComplete = progressList.get(progressList.size() - 1);
            progressBar.setValue(percentComplete.intValue());
        }

        @Override
        protected void done() {
            System.out.println("doInBackground is complete");
            if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
                System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
            }
            try {
//Here, the SwingWorker's get method returns an item of the same type as specified as the first type parameter
//given to the SwingWorker class.
                ArrayList<Integer> results = get();
                for (Integer i : results) {
                    listModel.addElement(i.toString());
                }
            } catch (Exception e) {
                System.out.println("Caught an exception: " + e);
            }
            startButton();
        }

        boolean IsPrime(int num) { //Checks whether a number is prime
            int i;
            for (i = 2; i <= num / 2; i++) {
                if (num % i == 0) {
                    return false;
                }
            }
            return true;
        }

        protected Integer FindNextPrime(int num) { //Returns next prime number from passed arg.       
            do {
                if (num % 2 == 0) {
                    num++;
                } else {
                    num += 2;
                }
            } while (!IsPrime(num));
            return new Integer(num);
        }
    }

    private JButton makeButton(String caption) {
        JButton b = new JButton(caption);
        b.setActionCommand(caption);
        b.addActionListener(this);
        getContentPane().add(b);
        return b;
    }

    private JProgressBar makeProgressBar(int min, int max) {
        JProgressBar progressBar1 = new JProgressBar();
        progressBar1.setMinimum(min);
        progressBar1.setMaximum(max);
        progressBar1.setStringPainted(true);
        progressBar1.setBorderPainted(true);
        getContentPane().add(progressBar1);
        return progressBar1;
    }

    private void startButton() {
        startButton.setEnabled(true);
        stopButton.setEnabled(false);
        System.out.println("SwingWorker - Done");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if ("Start" == null ? e.getActionCommand() == null : "Start".equals(e.getActionCommand())) {
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
// Note that it creates a new instance of the SwingWorker-derived class. Never reuse an old one.
            (swingWorker = new mySwingWorker()).execute(); // new instance
        } else if ("Stop" == null ? e.getActionCommand() == null : "Stop".equals(e.getActionCommand())) {
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
            swingWorker.cancel(true); // causes isCancelled to return true in doInBackground
            swingWorker = null;
        }
    }

    public static void main(String[] args) {
// Notice that it kicks it off on the event-dispatching thread, not the main thread.
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                SwingWorkerExample swingWorkerExample = new SwingWorkerExample();
            }
        });
    }
}

【讨论】:

  • isCancelled()方法在cancel()方法之后返回true,但是为什么线程'SwingWorker-pool-1-thread-1'仍然出现在执行中?
  • 这里给出了很好的解释(+1 全部)。这个例子是巨大的,但我喜欢它谢谢:)
  • 这个有趣的是借助JProgressBar 来解释逻辑的东西,很好:-)
  • 点击取消后不会输出不完整的结果到listModel。日志:doInBackground is completeSwingWorker - DonedoInBackground is completeSwingWorker - isCancelledCaught an exception: java.util.concurrent.CancellationExceptionSwingWorker - Done
  • 在那里找到stackoverflow.com/questions/6204141/…,'done()' 没有在预期的时间调用,然后你取消 swingworker
【解决方案3】:

默认情况下,SwingWorker 重用工作线程,因此即使doInBackground() 已返回,仍然可以看到执行您的方法的线程,这是完全正常的。

您可以通过查看 NetBeans 报告的线程名称来识别这一事实:SwingWorker-pool-1-thread-1,其中 poolSwingWorker 管理。

如果您想要更多控制权,您还可以将SwingWorker 实例传递给Executor

请查看SwingWorkerExecutor javadoc 了解更多信息。

此外,SwingWorker.cancel() 不应该从 doInBackground() 调用,而是从另一个线程调用,通常是 EDT,例如当用户点击进度对话框中的取消按钮时。

【讨论】:

  • 它不是重复使用的,每次我单击将在后台运行的进程的按钮时,它都会创建一个新线程。我会尝试从另一个线程调用,看看会发生什么。
  • 很难说,但你无法掌握SwingWorker 处理线程池以及将给定SwingWorker 实例分配给池中线程的方式。正如我所说,如果您想要完全控制,那么您可以使用Executor 的现有实现之一,或者构建您自己的实现,并将您的SwingWorker 实例传递给它的execute 方法。
  • 同意,但是 Executor 加上 SwingWorker 1) 仍然在 top25 bugs.sun.com/top25_bugs.do 2) 如果你想要真正的多线程,那么你必须命名你的线程 3) 为 SwingWorker 添加 PropertyChangeListener,然后你可以知道的状态SwingWorker 4) 对从 Executor 启动的同时线程数量充满信心,因为 Executor 不知道 SwingWorker 是否结束 5) 避免将任何故障从此处放置 boolean 布尔值,然后仅检查 PropertyChangeListener 中的值,最后一行从 done (),返回 true 一个确认线程结束没有任何错误
  • @mKorbel SwingWorker 的顶级错误如果我没记错的话与将它与 Executor 一起使用无关,它实际上是一个“纯粹的” SwingWorker 错误。
  • 是的,毕竟我想我会放弃 SwingWorker 并另谋出路。我只是对 SwingWorker 很好奇,但如果我需要完全控制,最好使用 Executors 或其他东西。谢谢大家。
【解决方案4】:

仅供参考:当SwingWorker 被取消时,done()doInBackground() 返回之前被调用。

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514

杰夫

【讨论】:

    【解决方案5】:

    您需要经常在 doInBackground() 代码中添加Thread.sleep(1) 调用。在你的 sleep catch 块中,添加return。我有同样的问题,这就像一个魅力。

    来源: https://blogs.oracle.com/swinger/entry/swingworker_stop_that_train

    【讨论】:

    • 感谢分享,很有用!
    【解决方案6】:

    直到 SwingWoker 它修复了 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6826514 这里是一个简单(测试)版本,具有基本(类似)功能,然后是 SwingWoker

    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package tools;
    
    import java.util.LinkedList;
    import java.util.List;
    import javax.swing.SwingUtilities;
    
    /**
     *
     * @author patrick
     */
    public abstract class MySwingWorker<R,P> {
    
        protected abstract R doInBackground() throws Exception;
        protected abstract void done(R rvalue, Exception ex, boolean canceled);
        protected void process(List<P> chunks){}
        protected void progress(int progress){}
    
        private boolean cancelled=false;
        private boolean done=false;
        private boolean started=false;
        final private Object syncprogress=new Object();
        boolean progressstate=false;
        private int progress=0;
        final private Object syncprocess=new Object();
        boolean processstate=false;
        private LinkedList<P> chunkes= new LinkedList<>();
    
        private Thread t= new Thread(new Runnable() {
            @Override
            public void run() {
                Exception exception=null;
                R rvalue=null;
                try {
                    rvalue=doInBackground();
                } catch (Exception ex) {
                    exception=ex;
                }
    
                //Done:
                synchronized(MySwingWorker.this)
                {
                    done=true;
                    final Exception cexception=exception;
                    final R crvalue=rvalue;
                    final boolean ccancelled=cancelled;
    
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            done(crvalue, cexception, ccancelled);
                        }
                    });
                }
    
            }
        });    
    
        protected final void publish(P p)
        {
            if(!Thread.currentThread().equals(t))
                throw new UnsupportedOperationException("Must be called from worker Thread!");
            synchronized(syncprocess)
            {
                chunkes.add(p);
                if(!processstate)
                {
                    processstate=true;
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            List<P> list;
                            synchronized(syncprocess)
                            {
                                MySwingWorker.this.processstate=false;
                                list=MySwingWorker.this.chunkes;
                                MySwingWorker.this.chunkes= new LinkedList<>();
                            }
                            process(list);
                        }
                    });
                }
            }
        }
    
        protected final void setProgress(int progress)
        {
            if(!Thread.currentThread().equals(t))
                throw new UnsupportedOperationException("Must be called from worker Thread!");
            synchronized(syncprogress)
            {
                this.progress=progress;
                if(!progressstate)
                {
                    progressstate=true;
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            int value;
                            //Acess Value
                            synchronized(syncprogress)
                            {
                                MySwingWorker.this.progressstate=false;
                                value=MySwingWorker.this.progress;
                            }
                            progress(value);
                        }
                    });
                }
            }
        }
    
        public final synchronized void execute()
        {
            if(!started)
            {
                started=true;
                t.start();
            }
        }
    
        public final synchronized boolean isRunning()
        {
            return started && !done;
        }
    
        public final synchronized boolean isDone()
        {
            return done;
        }
    
        public final synchronized boolean isCancelled()
        {
            return cancelled;
        }
    
        public final synchronized void cancel()
        {
            if(started && !cancelled && !done)
            {
                cancelled=true;
                if(!Thread.currentThread().equals(t))
                    t.interrupt();
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      • 2011-05-09
      相关资源
      最近更新 更多