从 EDT (EventQueue) 停止另一个线程
Stopping another thread from the EDT (EventQueue)
所以我创建了一个带有停止按钮的基本摆动界面,单击它应该会停止计数线程。当应用程序启动时,线程实例将分配一个 runnable
class 来执行计数循环并将其记录在控制台中。 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;
}
}
因为它首先要完成内层的for循环
for (int i = 0; i < 100000; ++i)
在进入外层 while 循环的下一次迭代之前再次检查 keepRunning
布尔值。
只需删除 for 循环即可。
设置停止条件时,经常检查条件很重要。目前,您有一个运行一段时间的循环,您只能在循环结束时看到检查结果。
要确保循环在迭代中结束,您可以在循环内添加以下检查。
if(!keepRunning) {
break;
}
您还可以尝试不同的解决方案。因为这种停止线程的模式经常被使用,你也可以为你的子任务使用一个 SwingWorker
这个 class 提供了相当多的有用的实用方法来停止,并更新你的 gui 线程安全子任务(例如,您可以在图形用户界面而不是命令行中显示计数)
更改代码以使用 SwingWorker
很容易,在扩展 SwingWorker
时,它需要 2 个元素,一个 "final result" 和一个 "pending result".
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(确保您导入正确的)class 可能更容易,使用 class 就像:
一样简单
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()
重新启动它,这与我向您展示的其他解决方案不同。
所以我创建了一个带有停止按钮的基本摆动界面,单击它应该会停止计数线程。当应用程序启动时,线程实例将分配一个 runnable
class 来执行计数循环并将其记录在控制台中。 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;
}
}
因为它首先要完成内层的for循环
for (int i = 0; i < 100000; ++i)
在进入外层 while 循环的下一次迭代之前再次检查 keepRunning
布尔值。
只需删除 for 循环即可。
设置停止条件时,经常检查条件很重要。目前,您有一个运行一段时间的循环,您只能在循环结束时看到检查结果。
要确保循环在迭代中结束,您可以在循环内添加以下检查。
if(!keepRunning) {
break;
}
您还可以尝试不同的解决方案。因为这种停止线程的模式经常被使用,你也可以为你的子任务使用一个 SwingWorker
这个 class 提供了相当多的有用的实用方法来停止,并更新你的 gui 线程安全子任务(例如,您可以在图形用户界面而不是命令行中显示计数)
更改代码以使用 SwingWorker
很容易,在扩展 SwingWorker
时,它需要 2 个元素,一个 "final result" 和一个 "pending result".
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(确保您导入正确的)class 可能更容易,使用 class 就像:
一样简单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()
重新启动它,这与我向您展示的其他解决方案不同。