如何正确地停止Java中的线程?

我需要一个解决scheme来正确地停止Java中的线程。

我有IndexProcessor类实现Runnable接口:

 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."); } } } 

但是当我closurestomcat,我得到我的IndexProcessor类的exception:

 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。 所以问题是:

我怎样才能停止线程,而不是抛出任何exception?

PS我不想使用.stop(); 方法,因为它已被弃用。

IndexProcessor类中,需要设置一个标志来通知线程需要终止的方法,类似于刚在类范围中使用的variablesrun

当你想停止线程,你设置这个标志,并调用线程上的join() ,并等待它完成。

通过使用volatilevariables或者使用与被用作标志的variables同步的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 Channel操作),那么你实际上可以立即跳出这些。

如果你使用一个标志,你必须等待阻止操作完成,然后你可以检查你的标志。 在某些情况下,您必须执行此操作,例如使用不可中断的标准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()

*:期望是这应该停止一个线程。 但是,线程在发生这种情况时究竟做了什么,完全取决于开发人员在创build线程实现时所写的内容。

在run方法实现中看到的常见模式是while(boolean){} ,其中boolean通常是名为isRunning东西,它是它的线程类的成员variables,它是volatile的,并且通常由其他线程通过setter方法sorts,例如kill() { isRunnable=false; } kill() { isRunnable=false; } 。 这些子程序很好,因为它们允许线程在终止之前释放它所拥有的任何资源。

你应该总是通过在run()循环中检查一个标志来结束线程(如果有的话)。

你的线程应该是这样的:

 public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); private volatile boolean execute; @Override public void run() { this.execute = true; while (this.execute) { try { LOGGER.debug("Sleeping..."); Thread.sleep((long) 15000); LOGGER.debug("Processing"); } catch (InterruptedException e) { LOGGER.error("Exception", e); this.execute = false; } } } public void stopExecuting() { this.execute = false; } } 

然后你可以通过调用thread.stopExecuting()来结束这个线程。 这样线程结束干净,但这需要15秒(由于你的睡眠)。 如果真的很紧急,你仍然可以调用thread.interrupt(),但是首选的方式应该是检查标志。

为了避免等待15秒,你可以像这样分开睡眠:

  ... try { LOGGER.debug("Sleeping..."); for (int i = 0; (i < 150) && this.execute; i++) { Thread.sleep((long) 100); } LOGGER.debug("Processing"); } catch (InterruptedException e) { ... 

为了同步线程,我更喜欢使用CountDownLatch ,它帮助线程等待进程完成。 在这种情况下,worker类是通过CountDownLatch实例设置的。 await方法的调用将被阻塞,直到当前计数由于调用countDown方法或达到超时设置而达到零。 这种方法可以立即中断一个线程,而无需等待指定的等待时间。

 public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); private final CountDownLatch countdownlatch; public IndexProcessor(CountDownLatch countdownlatch) { this.countdownlatch = countdownlatch; } public void run() { try { while (!countdownlatch.await(15000, TimeUnit.MILLISECONDS)) { LOGGER.debug("Processing..."); } } catch (InterruptedException e) { LOGGER.error("Exception", e); run = false; } } } 

当你想完成另一个线程的执行时,在CountDownLatch上执行countDown并将线程join主线程:

 public class SearchEngineContextListener implements ServletContextListener { private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class); private Thread thread = null; private IndexProcessor runnable = null; private CountDownLatch countdownLatch = null; @Override public void contextInitialized(ServletContextEvent event) { countdownLatch = new CountDownLatch(1); Thread thread = new Thread(new IndexProcessor(countdownLatch)); 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 (countdownLatch != null) { countdownLatch.countDown(); } if (thread != null) { try { thread.join(); } catch (InterruptedException e) { LOGGER.error("Exception", e); } LOGGER.debug("Thread successfully stopped."); } } } 

一些补充信息。 Java文档中build议使用标志和中断。

https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

 private volatile Thread blinker; public void stop() { blinker = null; } public void run() { Thread thisThread = Thread.currentThread(); while (blinker == thisThread) { try { Thread.sleep(interval); } catch (InterruptedException e){ } repaint(); } } 

对于等待很长时间的线程(例如input),使用Thread.interrupt

 public void stop() { Thread moribund = waiter; waiter = null; moribund.interrupt(); } 

我没有得到在Android的中断工作,所以我用这个方法,完美的作品:

 boolean shouldCheckUpdates = true; private void startupCheckForUpdatesEveryFewSeconds() { threadCheckChat = new Thread(new CheckUpdates()); threadCheckChat.start(); } private class CheckUpdates implements Runnable{ public void run() { while (shouldCheckUpdates){ System.out.println("Do your thing here"); } } } public void stop(){ shouldCheckUpdates = false; } 

如果我们使用的是JDK 1.0,我们可以调用Thread的不推荐使用的方法stop()来终止它。 使用stop()是非常危险的,因为即使它处于重要的事情中,它也会杀死你的线程。 没有办法保护自己,所以如果你发现使用stop()的代码,你应该皱眉头。

我们如何closures一个线程干净?

在Java中,启动线程很容易,但closures它们需要很多的关注和努力。

这是如何在Java中devise的。 在每个java线程中都有一个叫做中断状态标志的标志,我们可以从外部设置它,也就是父主线程或主线程。 线程可能会偶尔检查并停止执行。 自愿.. !! 这里是如何:

 Thread loop = new Thread(new Runnable() { @Override public void run() { while (true) { if (Thread.interrupted()) { break; } // Continue to do nothing } }}); loop.start(); loop.interrupt(); 

来源: 如何停止在Java |的线程 multithreading