我知道如何优雅地结束一个线程:
public class Foo implements Runnable {
private volatile boolean stop = false;
public void stop() {
stop = true;
}
public void run() {
while (!stop) {
...
}
}
}
但是如果某个线程正在等待某些东西,在某个对象中(使用wait()
,没有时间限制),那么这个结构对于停止这个线程来说是没有用的,因为他已经超过了while循环中的条件,所以他将永远继续下去。
如果线程实际上正在等待某事,则应该调用Thread.interrupt()方法来中断线程。不要在while循环中检查自定义变量,而是使用Thread.isInterrupted()或Thread.interrupted()——一个重置中断标志,另一个不重置。
如果您正在等待某事,我认为您必须捕获InterruptedException,不是吗?
如果您不希望您的线程无限期地等待,那么首先就不要让它们这样做。你在写他们的代码,所以你要把它写在你实际做的事情上,这样你就不必试图从外部修复它。
每个行为良好的阻塞方法都声明一个检查异常InterruptedException
,它的确切目的是:通知线程在阻塞时被中断。
你必须捕获这个异常,实际上这可以取代你的stop
字段。
例如,让我们考虑一个日志系统,它将消息写入一个专用线程上的文件(以便在IO上花费的时间不会干扰您的应用程序—假设它不是IO繁重)。
每个线程都有一个中断标志,可以通过Thread.currentThread().isInterrupted()
读取。你可以尝试这样做:
class Logger {
private final File file = ...;
private final BlockingQueue<String> logLines = new LinkedBlockingQueue<String>();
private final Thread loggingThread = new Thread(new Runnable(){
@Override public void run() {
PrintWriter pw;
try {
pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
while (!Thread.currentThread().isInterrupted()) {
try {
pw.println(logLines.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // good habit: make sure the interrupt status is set
}
}
pw.flush();
pw.close();
} catch (IOException e) { ... flush and close pw if not null and open ... }
}
});
{ loggingThread.start(); }
public void log(final String line) { logLines.offer(line); } // will always work, because logLines is a LinkedBQ.
public void stop() { loggingThread.interrupt(); }
}
最后,为了安全关闭应用程序,必须确保在JVM关闭之前终止该线程。要做到这一点,您必须在以任何可能的方式关闭之前绝对确定调用stop()
,或者您可以通过向类中添加类似于以下实例初始化器的内容来注册一个关闭钩子:
class Logger {
...
{
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override public void run() { close(); }
}));
}
}
这将迫使JVM在终止之前调用close()(因此中断线程,刷新并关闭文件)。
这完全取决于你为什么要在线程中等待。如果线程正在等待不可中断的IO,那么您可以查看在等待套接字
输入时阻塞的Stop/Interrupt线程否则一切都取决于你如何在线程中等待。您可以使用wait(1000)
,然后检查标志并等待更多时间。您可以等待来自阻塞队列的消息,您可以使用锁/条件,甚至wait/notify
也可以在这种情况下工作,您需要正确处理中断。