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

标签 java multithreading concurrency eventqueue edt

因此,我创建了一个带有停止按钮的基本 Swing 界面,单击它应该停止计数线程。当应用程序启动时,线程实例将分配一个可运行的类,该类执行计数循环并将其记录在控制台中。 runnable 接口(interface)中有一个方法将 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;
    }
}

最佳答案

设置停止条件时,经常检查该条件非常重要。目前,您有一个运行一段时间的循环,并且您只能在循环末尾看到检查结果。

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

if(!keepRunning) {
    break;
}

您也可以尝试不同的解决方案。由于这种停止线程的模式经常使用,因此您还可以使用 SwingWorker对于您的子任务,此类提供了大量有用的实用程序方法,用于停止和更新子任务中安全的 gui 线程(例如,您可以在 gui 而不是命令行中显示计数)

更改代码以使用 SwingWorker 很容易,在扩展 SwingWorker 时,它需要 2 个元素,一个“最终结果”和一个“待定结果”。

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 可能会更容易(确保导入正确的)类,使用该类就像这样简单:

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() 再次重新启动它,这与我向您展示的其他解决方案不同。

关于java - 从 EDT (EventQueue) 停止另一个线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45854531/

相关文章:

java - Gson 缺少对象 ArrayList 的类型参数

java - HashMap 中唯一键的线程安全性

ruby-on-rails-3 - 在 Rails 3 应用程序上测试并发访问

java - 使用 Ant 编译 Eclipse 项目时出现问题

Java将xml属性放在HTTP请求的 "request version"部分

java - 通过 nanoTime() 的时间

java - 线程和事件

java - 使用 Java 并发运行多线程任务,直到有足够的输出

c++在第一类的构造函数中创建第二类的对象 - 多线程

java - 在 Java 中计算 ConcurrentHashmap 中的 delta