为什么在Java中停止线程如此困难?
我再次遇到这样一种情况,即无法停止/销毁/挂起线程。中断()不起作用,.stop()和.suspend()不推荐使用 非常简单的例子:为什么在Java中停止线程如此困难?,java,multithreading,Java,Multithreading,我再次遇到这样一种情况,即无法停止/销毁/挂起线程。中断()不起作用,.stop()和.suspend()不推荐使用 非常简单的例子: public class TimerThread extends Thread { private JPanel colorPanel; public TimerThread(JPanel colorPanel) { this.colorPanel = colorPanel; } public void ru
public class TimerThread extends Thread {
private JPanel colorPanel;
public TimerThread(JPanel colorPanel) {
this.colorPanel = colorPanel;
}
public void run() {
while (true) {
try {
Thread.sleep(1000);
colorPanel.repaint();
} catch (Exception ex) {
//do Nothing
}
}
}
}
这样做的目的是每秒重新绘制一个特定的JPanel以改变其颜色。我想从另一个类开始和停止线程,如下所示:
timer = new Thread(new TimerThread(colorPanel));
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.interrupt();
}
});
显然(?)这不起作用。。。我知道我可以使用计时器、SwingWorker或将计时器声明为Timer=new Timer读取(colorPanel)
并在run方法中使用布尔值而不是“true”,但我被要求将计时器声明为“Thread”而不是其他
令我惊讶的是(或者这有那么愚蠢吗?),即使这样也不起作用:
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer = new Thread(new TimerThread(colorPanel));
timer.start();
}
});
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.interrupt();
timer = null;
}
});
所以我的问题很简单:如何让线程在Java中启动/暂停/恢复/停止?基本上是让它们协同工作。你有一些共享的标志,让他们看到他们应该做什么,当你睡觉的时候,你可以在一些共享的监视器上等待。然后,当您想要控制线程时,您可以设置适当的标志并通知监视器,这样,如果线程正在等待,它就会醒来并注意到它应该挂起/停止/无论什么。显然,您需要对共享状态采取常规的谨慎措施,使用易失性变量、原子*对象或锁定来确保每个线程都能看到其他线程所做的更新
任何不合作的操作都是有风险的,因为有可能在操作进行到一半时破坏状态。预先停止线程是危险的。这样做会导致死锁、资源泄漏等等。相反,您应该使用协作信令机制
向线程发出您希望它停止的信号,然后等待它停止。线程应定期检查是否需要停止并做出相应的反应。当您收到中断时,应开始清理并返回a.s.a.p.(或至少重置中断状态) 另一种方法是在条件中检查
Thread.interrupted()
(但需要在InterruptedException捕获中重置中断状态)
但是,在swing中,您可以让事件每隔一段时间运行一次,并使用该事件的api停止该事件
javax.swing.Timer timer = new Timer(1000,new ActionListener() {
public void actionPerformed(ActionEvent e) {
colorPanel.repaint();
}
});
timer.setRepeats(true);
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.stop();
}
});
试试这个:
public class TimerThread extends Thread {
private volatile boolean stop = false;
private JPanel colorPanel;
public TimerThread(JPanel colorPanel) {
this.colorPanel = colorPanel;
}
public void stopTimer() {
stop = true;
}
public void run() {
while (stop == false) {
try {
Thread.sleep(1000);
colorPanel.repaint();
} catch (Exception ex) {
//do Nothing
}
}
}
}
// Why new Thread(new TimerThread(...))?
// timer = new Thread(new TimerThread(colorPanel));
timer = new TimerThread(colorPanel)
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.stopTimer();
}
});
另外,还可以查看如何复制已弃用的
stop
。正确的方法确实是使用一个变量来确定何时停止线程,退出其run方法。您可以找到有关如何正确执行此操作的更多信息,而不是在while(true)时循环
,您应该在线程未中断时循环:
@Override public void void() {
// some kind of initialization...
while (!Thread.currentThread().isInterrupted()) {
try { ...
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // ensure interrupt flag is set
}
}
// some kind of cleanup
}
如果
InterruptedException
在块中没有被中的任何东西抛出,那么要么您不使用阻塞操作(并且在这个线程上简单地调用Thread.interrupt()将在下一次迭代中停止它),要么您使用一些行为不好的阻塞调用(JCL本身中有许多这样的示例!).使用此解决方案,您不会获得通过等待/通知或中断可以获得的“即时”更新,但如果您不介意一秒钟的延迟,它应该可以完成这项工作
volatile boolean stopped = false;
volatile boolean paused = false;
pauseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
paused = true;
}
});
resumeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
paused = false;
}
});
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stopped = true;
}
});
... TimerThread
public void run() {
while (stopped == false) {
try {
Thread.sleep(1000);
if (stopped)
break;
if (!paused)
colorPanel.repaint();
} catch (Exception ex) {
//do Nothing
}
}
}
如果你一直遇到这种情况,你需要重新思考如何使用线程。线程应该控制自己的生命周期,而不是在外部暂停/停止。这是一个设计问题。@skaffman再次阅读OP:这是为了家庭作业,他被迫只使用一个线程。事实上,@tot2,如果更高的顺序命令你使用线程y,这可能是多么不幸你不能交付用SwingWorker实现的东西…@Matthias:因为Java在这方面有点糟糕?当你被锁定在某个第三方库中时,问题尤其严重,因为你无法控制它,它会阻止你的线程。在构思良好的系统上,你可以杀死任何你喜欢的东西。例如,在Unx系统上,你可以对一个进程发出*kill-9,你就可以保证不仅杀死该进程,还可以取回该进程使用的所有资源。Java不允许破坏流氓线程,这真是太糟糕了。我曾经有过这样的情况,我生成了一个Java进程,并在它变成流氓时以编程方式杀死它。为什么要创建新线程(new TimerThread(…)?因为我只能使用“线程”。如果我尝试启动、停止然后再次启动它,这不会引发异常吗?我的解决方案不是重新启动线程。您可以覆盖Start
,将Stop
设置回false
,然后调用super.Start()
@Matthias那么你的类TimerThread应该实现Runnable,而不是扩展Thread类,你可以将Runnable实例传递给Thread构造函数。
volatile boolean stopped = false;
volatile boolean paused = false;
pauseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
paused = true;
}
});
resumeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
paused = false;
}
});
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stopped = true;
}
});
... TimerThread
public void run() {
while (stopped == false) {
try {
Thread.sleep(1000);
if (stopped)
break;
if (!paused)
colorPanel.repaint();
} catch (Exception ex) {
//do Nothing
}
}
}