[英]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.