我需要一个解决方案来正确停止Java中的线程。
我有IndexProcessor实现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启动和停止线程的类:
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类中得到了异常:
tomcat
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();方法,因为它已过时。
.stop()
在IndexProcessor类中,你需要一种设置标志的方法,该标志通知线程它将需要终止,类似于run你刚刚在类范围中使用的变量。
run
当你希望停止线程时,可以设置该标志并join()在线程上调用并等待其完成。
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:
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 Channel操作),则实际上可以立即摆脱这些干扰。
Thread.interrupt()
Thread.sleep
java.nio Channel
如果使用标志,则必须等待阻止操作完成,然后才能检查标志。在某些情况下,无论如何你都必须这样做,例如使用标准的InputStream/ OutputStream不可中断。
InputStream/ OutputStream
在这种情况下,当线程被中断时,它不会中断IO,但是,你可以轻松地在代码中常规执行此操作(并且应该在可以安全地停止和清理的关键时刻执行此操作)
if (Thread.currentThread().isInterrupted()) { // cleanup and stop execution // for example a break in a loop }
就像我说的那样,这样做的主要优点Thread.interrupt()是你可以立即中断可中断的调用,而使用标志方法是无法做到的。