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

新手上路,请多包涵

我需要一个解决方案来正确停止 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.");
        }
    }
}

但是当我关闭 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(); 方法,因为它已被弃用。

原文由 Paulius Matulionis 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 365
2 个回答

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

原文由 DrYap 发布,翻译遵循 CC BY-SA 3.0 许可协议

使用 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() 的主要优点是您可以立即中断可中断的调用,这是标志方法无法做到的。

原文由 Matt 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题