简体   繁体   English

停止来自EDT的另一个线程(EventQueue)

[英]Stopping another thread from the EDT (EventQueue)

So I created a basic swing interface with a stop button, and clicking it should stop a counting thread. 因此,我创建了一个带有停止按钮的基本摆动界面,单击该按钮应停止计数线程。 When the application starts, a thread instance would allocate a runnable class which does a counting loop and logs it in the console. 当应用程序启动时,线程实例将分配一个runnable类,该类进行计数循环并将其记录在控制台中。 There is a method in the runnable interface that sets the volatile variable to false that should basically stop the thread and I called it on the stop button, but why doesn't it stop the loop? runnable接口中有一种将volatile变量设置为false的方法,该方法基本上应该停止线程,我在停止按钮上调用了它,但是为什么不停止循环呢? Here is my code. 这是我的代码。

ParentContainer.java 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 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;
    }
}

Because it first wants to complete the inner for-loop 因为它首先要完成内部for循环

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

before going to the next iteration of the outer while-loop to check the keepRunning boolean again. 在转到外部while循环的下一次迭代之前,请再次检查keepRunning布尔值。

Simply removing the for-loop will do. 只需删除for循环即可。

When making a stop condition, it is important that you check the condition frequently. 设定停止条件时,经常检查该条件很重要。 At the moment, you have a loop that runs for a while, and you only see the result of your check at the end of the loop. 目前,您有一个循环运行了一段时间,并且您只能在循环结束时看到检查结果。

To make sure the loop ends mid-iteration, you can add the following check inside the loop. 为确保循环在迭代中结束,您可以在循环内添加以下检查。

if(!keepRunning) {
    break;
}

There are different solutions you can also try. 您也可以尝试其他解决方案。 Because this pattern of stopping threads is frequently used, you can also use a SwingWorker for your sub task this class gives a fair amount of useful utility methods for stopping, and for updating the gui thread safe from your subtask (so for example, you can show count in the gui instead of the command line) 因为这种停止线程的模式是经常使用的,所以您也可以为子任务使用SwingWorker ,此类提供了大量有用的实用程序方法来停止以及从子任务安全地更新gui线程(例如,您可以在gui中显示计数,而不是在命令行中显示)

Changing your code to use a SwingWorker is easy, when extending SwingWorker , it expects 2 elements, a "final result", and a "pending result". 扩展SwingWorker ,更改代码以使用SwingWorker很容易,它需要2个元素,即“最终结果”和“待处理结果”。

Example of SwingWorker : 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);

Notice that canceling with false is recommended, because when doing it with true means that the thread executing the counter will be interrupted, causing the Thread.sleep() to throw an exception. 请注意,建议使用false取消,因为使用true取消意味着执行计数器的线程将被中断,从而导致Thread.sleep()引发异常。

If the purpose of your task is only to count a number, it may be even easier to use a timer (Make sure you import the correct one) class instead, using the class is as easy as: 如果您的任务仅是计算一个数字,则使用计时器 (请确保您导入正确的计时器 )类可能更加容易,而使用该类则很简单:

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();

Notice that with this timer solution, you can restart it again by calling start() , unlike the other solutions I shown you. 注意,使用此计时器解决方案,您可以通过调用start()再次重新启动它,这与我向您展示的其他解决方案不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 停止事件调度线程(EventQueue)中的另一个线程 - Stopping another thread from an Event Dispatch Thread (EventQueue) 在Java中难以从另一个线程停止一个线程 - Difficulty stopping one thread from another in java Java:从另一个线程中停止一个线程中的所有活动 - Java: stopping all activities in a thread from another thread 从JFrame打开另一个JFrame实例:使用EventQueue.invokeLater吗? (事件调度线程) - Open another instance of JFrame (from a JFrame): use EventQueue.invokeLater? (event dispatch thread) 线程“ AWT-EventQueue-0”中的异常java.lang.NullPointerException。 问题从另一个类调用ActionListener - Exception in thread “AWT-EventQueue-0” java.lang.NullPointerException. Issue calling ActionListener from another class 使线程在EDT的非EDT(事件调度线程)线程上运行 - Make thread run on non EDT (event dispatch thread) thread from EDT 如何从java中的EventQueue访问一个线程 - how to access a thread from EventQueue in java 是否可以从EDT线程在非EDT线程上调用Future.get()? - Is it OK to call Future.get() on a non-EDT thread from the EDT thread? 同步方法在停止一个线程与另一个线程之间的重要性是什么? - What is the importance of synchronized method in stopping one Thread from another? 检查线是否需要EDT? - Check if thread is EDT is necessary?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM