从EDT停止另一个线程(EventQueue)

时间:2017-08-24 06:36:55

标签: java multithreading concurrency eventqueue edt

所以我创建了一个带有停止按钮的基本swing接口,点击它会停止计数线程。当应用程序启动时,线程实例将分配一个runnable类,该类执行计数循环并将其记录在控制台中。 runnable接口中有一个方法将volatile变量设置为false,基本上应该停止线程并在停止按钮上调用它,但为什么不停止循环?这是我的代码。

ParentContainer.java

public class ParentContainer extends JFrame implements ActionListener, WindowListener {
    private static final long serialVersionUID = 1L;
    private JButton btnStop;

    private static CountRunnable cr;

    public ParentContainer() {
        Container cp = getContentPane();
        cp.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
        btnStop = new JButton("Stop Counting");
        cp.add(btnStop);

        SymAction lSymAction = new SymAction();
        btnStop.addActionListener(lSymAction);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Counter");
        setSize(200, 90);
        setVisible(true);
    }

    public static void main(String[] args) {
        // Run GUI codes in Event-Dispatching thread for thread safety
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ParentContainer(); // Let the constructor do the job
            }
        });

        cr = new CountRunnable();
        Thread t1 = new Thread(cr, "MyRunnable");
        t1.start();
    }

    class SymAction implements ActionListener
    {
        public void actionPerformed(ActionEvent evt)
        {
            Object object = evt.getSource();
            if (object == btnStop) { btnStop_actionPerformed(evt); }
        }
    }

    void btnStop_actionPerformed(ActionEvent evt)
    {
        cr.stop();
    }

    @Override
    public void windowActivated(WindowEvent arg0) { }

    @Override
    public void windowClosed(WindowEvent arg0) { }

    @Override
    public void windowClosing(WindowEvent arg0) { }

    @Override
    public void windowDeactivated(WindowEvent arg0) { }

    @Override
    public void windowDeiconified(WindowEvent arg0) { }

    @Override
    public void windowIconified(WindowEvent arg0) { }

    @Override
    public void windowOpened(WindowEvent arg0) { }

    @Override
    public void actionPerformed(ActionEvent arg0) { }
}

CountRunnable.java

public class CountRunnable implements Runnable {
    public static volatile boolean keepRunning;
    private int count = 1;

    @Override
    public void run() {
        keepRunning = true;
        while (keepRunning)
        {
            for (int i = 0; i < 100000; ++i) {
                System.out.println(count + "");
                ++count;
                // Suspend this thread via sleep() and yield control to other threads.
                // Also provide the necessary delay.
                try {
                    Thread.sleep(10); // milliseconds
                }
                catch (InterruptedException ex) {
                }
            }
        }
    }

    public void stop()
    {
        keepRunning = false;
    }
}

2 个答案:

答案 0 :(得分:1)

因为它首先要完成内部for循环

for (int i = 0; i < 100000; ++i)

在进入外部while循环的下一次迭代之前再次检查keepRunning布尔值。

只需删除for循环即可。

答案 1 :(得分:1)

在停止状态时,请务必经常检查状况。目前,你有一个运行一段时间的循环,你只能在循环结束时看到你的检查结果。

为确保循环在迭代中结束,您可以在循环内添加以下检查。

if(!keepRunning) {
    break;
}

您也可以尝试不同的解决方案。因为经常使用这种停止线程的模式,所以你也可以使用SwingWorker作为你的子任务,这个类给出了相当多的有用的实用方法来停止,并且从子任务中更新gui线程安全(所以对于例如,您可以在gui中显示count而不是命令行)

更改代码以使用SwingWorker很容易,在扩展SwingWorker时,它需要2个元素,a&#34;最终结果&#34;以及&#34;待定结果&# 34。

SwingWorker的示例:

SwingWorker<String, String> task = new SwingWorker<String, String>(){
    private int count = 1;

    @Override
    public List<Integer> doInBackground() throws Exception {
        while (!isCancelled()) {
            for (int i = 0; i < 100000; ++i) {
                publish(count + "");
                ++count;
                Thread.sleep(10); // milliseconds
            }
        }
        return count + "";
    }

    @Override
    protected void process(List<Integer> chunks) {
        gui.setText(chunk.get(chunk.size() - 1));
    }

    @Override
    protected void done() {
        try {
            gui.setText(this.get());
        } catch(InterruptedException | ExecutionException ex) {
            ex.printSTackTrace();
        }
    }
}
task.execute();

// Later:

task.cancel(false);

请注意,建议使用false取消,因为使用true表示执行计数器的线程将被中断,导致Thread.sleep()抛出异常。

如果您的任务的目的只是计算一个数字,那么使用timer(确保导入正确的)类可能更容易,使用该类就像以下一样简单: / p>

int delay = 10; //milliseconds
ActionListener taskPerformer = new ActionListener() {
    int count = 0;
    public void actionPerformed(ActionEvent evt) {
        count++;
        gui.setText(count + "");
        // System.out.println(count + "");
    }
};
Timer task = new Timer(delay, taskPerformer);
task.setCoalesce(false); // SOmetimes, executions can be missed because other programs 
task.setRepeating(true);
task.start();

// Later:
task.stop();

请注意,使用此计时器解决方案,您可以通过调用start()重新启动它,这与我向您展示的其他解决方案不同。