1

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

父容器.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;
    }
}
4

2 回答 2

1

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

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

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

只需删除 for 循环即可。

于 2017-08-24T06:44:37.027 回答
1

制作停止条件时,经常检查该条件很重要。目前,您有一个运行了一段时间的循环,您只能在循环结束时看到检查结果。

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

if(!keepRunning) {
    break;
}

您也可以尝试不同的解决方案。因为这种停止线程的模式经常被使用,你也可以SwingWorker为你的子任务使用这个类提供了相当多有用的实用方法来停止和更新你的子任务中安全的 gui 线程(例如,你可以显示在 gui 而不是命令行中计数)

将代码更改为使用 aSwingWorker很容易,在扩展 时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() 抛出异常。

如果您的任务的目的只是计算一个数字,则使用计时器(确保导入正确的)类可能会更容易,使用该类就像:

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

于 2017-08-24T07:20:00.087 回答