如何正确地停止Java中的线程?
我需要一个解决方案来正确地停止Java中的线程。
我有实现Runnable接口的IndexProcessor
类:
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
@Override
public void run() {
boolean run = true;
while (run) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
}
我有ServletContextListener
类,它启动和停止线程:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
@Override
public void contextInitialized(ServletContextEvent event) {
thread = new Thread(new IndexProcessor());
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
thread.interrupt();
LOGGER.debug("Thread successfully stopped.");
}
}
}
但是当我关闭tomcat时,我在我的IndexProcessor类中遇到异常:
2012-06-09 17:04:50,671 [Thread-3] ERROR IndexProcessor Exception
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.java:22)
at java.lang.Thread.run(Unknown Source)
我正在使用JDK 1.6。 所以问题是:
我怎样才能阻止线程并且不抛出任何异常?
PS我不想使用.stop();
方法,因为它已被弃用。
在IndexProcessor
类中,您需要设置一个标志来通知线程它将需要终止,类似于您在类范围中使用的变量run
。
当你想停止线程时,你设置这个标志并在线程上调用join()
并等待它完成。
通过使用volatile变量或者使用与被用作标志的变量同步的getter和setter方法,确保该标志是线程安全的。
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
private volatile boolean running = true;
public void terminate() {
running = false;
}
@Override
public void run() {
while (running) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
running = false;
}
}
}
}
然后在SearchEngineContextListener
:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
private IndexProcessor runnable = null;
@Override
public void contextInitialized(ServletContextEvent event) {
runnable = new IndexProcessor();
thread = new Thread(runnable);
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
runnable.terminate();
thread.join();
LOGGER.debug("Thread successfully stopped.");
}
}
}
使用Thread.interrupt()
是完全可以接受的方式。 事实上,如上所述,这可能是最好的一面旗帜。 原因是,如果你处于一个可中断的阻塞呼叫(如Thread.sleep
或使用java.nio频道操作),你实际上可以立即跳出这些呼叫。
如果您使用标志,则必须等待阻止操作完成,然后才能检查标志。 在某些情况下,您必须执行此操作,例如使用不可中断的标准InputStream
/ OutputStream
。
在这种情况下,当线程中断时,它不会中断IO,但是,您可以在代码中轻松地执行此操作(并且您应该在可以安全停止和清理的关键点执行此操作)
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}
就像我说的, Thread.interrupt()
的主要优点是你可以立即摆脱可中断的调用,这是你不能用标志方法做的。
简单的答案:您可以通过以下两种常用方式之一停止INTERNALLY线程:
你也可以停止EXTERNALLY线程:
system.exit
(这会杀死整个进程) interrupt()
方法* kill()
或stop()
) *:期望的是,这应该阻止一个线程。 但是,线程在发生这种情况时的实际作用完全取决于开发人员在创建线程实现时编写的内容。
您在运行方法实现中看到的常见模式是while(boolean){}
,其中布尔值通常是名为isRunning
东西,它是其线程类的成员变量,它是易失性的,并且通常可由其他线程通过setter方法sorts,例如kill() { isRunnable=false; }
kill() { isRunnable=false; }
。 这些子程序很好,因为它们允许线程在终止之前释放它保存的任何资源。