【问题标题】:SwingWorker done method throws cancellationexception with get()SwingWorker done 方法使用 get() 抛出取消异常
【发布时间】:2015-02-07 06:11:55
【问题描述】:

我遇到了为我的 gui 创建停止/启动 jbuttons 的问题,经过大量谷歌搜索后,我意识到我需要多线程。进一步阅读后,我发现了 swingworker 类,并且我设法让我的 GUI 响应 STOP 按钮。

现在我的问题是这样的

doinbackground() 方法执行一段代码,在无限 while 循环中捕获数据包,条件为 (!isCancelled),一旦它被取消(STOP 按钮执行 worker.cancel()),它会返回一个 ArrayList理论上,我应该能够使用 get() 在 done() 方法中获取数据包。正确的?但是当我尝试这样做时,我得到了一个 CancellationException,这让我现在发疯了。

任何帮助都将受到高度评价! 谢谢

编辑:obj 是在类外部声明的用于存储返回值的 ArrayList。

这是我通过 START jbutton 执行的代码

private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {


    final ArrayList packet_list = new ArrayList();
    obj.clear();

    try {
        worker = new SwingWorker<ArrayList,Integer>(){//initialze swingworker class


            @Override
            protected void done(){

                try {  

                    obj = get();

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

            }

            //opens up stuff required to capture the packets    
            NetworkInterface   [] devices = JpcapCaptor.getDeviceList();
            int index = (jComboBox5.getSelectedIndex()-1);
            JpcapCaptor captor =JpcapCaptor.openDevice(devices[4], 65535, false, 20); 

            @Override
            protected ArrayList doInBackground(){  

                while(!isCancelled()){

                    try {
                        Packet packets = captor.getPacket(); //captures packets

                        if (packets != null)  //filters out null packets
                        {

                            //System.out.println(packets);
                            packet_list.add(packets); //adds each packet to ArrayList
                        }
                        Thread.sleep(100);

                    } catch (InterruptedException ex) {
                        return packet_list;
                    }
                }
                return packet_list;

            }


        };

        worker.execute();
    } catch (IOException ex) {
        Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
    }
}                                 


The stop button simply executes 

worker.cancel(); no errors there. and this is the swingworker declaration

 private SwingWorker<ArrayList,Integer> worker;

【问题讨论】:

    标签: java multithreading user-interface swingworker


    【解决方案1】:

    cancel 不只是设置isCancelled 标志让您在闲暇时阅读。那将几乎没有用。 如果它还没有启动,它会阻止任务启动,如果它已经在运行,它可能会主动中断线程。因此,获得CancellationException自然的结果取消正在运行的任务。

    为了进一步说明这一点,isCancelled 上的 Javadoc 指出:

    如果此任务在正常完成之前被取消,则返回 true。

    因此,如果返回 true那么您的任务将无法正常完成。您不能取消任务并期望它照常继续。

    SwingWorker 文档说“在后台线程中执行冗长的 GUI 交互任务的抽象类”。但是,对于 GUI 和应用程序生命周期,“冗长”的定义是不同的。 100 毫秒的任务对于 GUI 来说非常长,最好由 SwingWorker 完成。 10 分钟的任务对于SwingWorker 来说太长了,因为它有一个有限的线程池,你可能会耗尽。从您的问题描述来看,您确实做到了 - 一个可能运行时间很长的任务。因此,您应该创建一个适当的后台线程,而不是使用SwingWorker

    在该线程中,您将拥有一个 AtomicBoolean 或只是一个 volatile boolean 标志,您可以从 EDT 手动设置。然后线程可以将事件连同结果一起发布到 EDT。

    代码:

    class PacketCaptureWorker implements Runnable {
        private volatile boolean cancelled = false;
        public void cancel() {
            cancelled = true;
        }
        public void run() {
            while (!cancelled) {
                //do work
            }
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    //Use the result of your computation on the EDT
                }
            });
        }
    }
    
    new Thread(new PacketCaptureWorker()).start();
    

    【讨论】:

    • 感谢您的快速响应!和代码。所以我应该忘记swingworker来完成这个特定的任务。我对线程几乎一无所知,所以首先我应该阅读一下。如果我再次遇到困难,我会跟进
    • 只是想知道,我不能使用 volatile 布尔值来测试 swingworker 方法的 while 循环内的条件吗?
    • @user269952 是的,你可以这样做,我相信它会解决你眼前的问题。 SwingWorker 中长时间运行的任务的问题是只能有 2 个并行运行(在当前实现中)。因此,如果您开始 2,任何其他人都将不得不等待。您目前似乎只有 1 个,这更像是“如果您添加功能,未来可能会成为问题”
    • 啊,我明白了。我决定像你建议的那样使用一个单独的线程。我使用了一个扩展的 Thread 类并将 invokeLater 方法放在一个单独的函数中。对于您的输入来说,它非常顺利。也学到了很多!欢呼
    【解决方案2】:

    我尝试在 swingworker 线程的 while 循环中使用 volatile 布尔值,而不是使用 worker.cancel(),它运行良好。 (至少在表面上)我也设法创建了一个普通的后台线程,这也很有效:D 非常感谢你让我头疼不已!想知道两者中最好的方法是什么。

    跟进,我必须使 volatile 布尔值可用于整个类,因为我必须为线程类创建 2 个单独的实例,一个使用 START,另一个使用 STOP。显然,两个不同的实例并不针对变量的同一个实例。这是不好的做法吗?

    【讨论】:

    • 新问题应该作为实际问题发布,而不是作为答案发布。 2 个实例仅在它们具有对所述变量的引用或该变量是静态的情况下才会共享一个变量。默认情况下,对象实例之间不共享变量。
    • 抱歉!我会记住的。谢谢你的澄清。
    猜你喜欢
    • 1970-01-01
    • 2011-08-17
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    • 2018-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多