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

我有IndexProcessorclass,它实现了可运行接口:

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

我如何停止线程而不抛出任何异常?

附注:我不想使用.stop();方法,因为它已弃用。


当前回答

简单的回答是: 你可以用以下两种方法之一在内部停止线程:

run方法命中返回子例程。 运行方法完成,并隐式返回。

你也可以从外部停止线程:

电话系统。退出(这会终止整个进程) 调用线程对象的interrupt()方法* 看看线程是否有一个听起来可行的方法(如kill()或stop())

*:期望是这应该停止一个线程。然而,当这种情况发生时,线程实际做什么完全取决于开发人员在创建线程实现时编写的内容。

你在run方法实现中看到的一个常见模式是while(boolean){},其中布尔值通常是名为isRunning的东西,它是其线程类的成员变量,它是volatile的,通常由其他线程通过sort setter方法访问,例如kill() {isRunnable=false;}。这些子例程很好,因为它们允许线程在终止之前释放它所持有的任何资源。

其他回答

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

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();
    }
}

对于需要长时间等待的线程(例如,等待输入),使用thread .interrupt

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

您应该始终通过检查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. stopexecution()结束线程。这样线程就干净地结束了,但这需要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) {
        ...

通常,线程在被中断时被终止。那么,为什么不使用本机布尔值呢?尝试isInterrupted ():

Thread t = new Thread(new Runnable(){
        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()){
                // do stuff         
            }   
        }});
    t.start();

    // Sleep a second, and then interrupt
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {}
    t.interrupt();

我如何杀死一个线程?不使用stop();

使用Thread.interrupt()是一种完全可以接受的方法。事实上,它可能比上面建议的标志更可取。原因是,如果你在一个可中断的阻塞调用(如线程。睡眠或使用java。nio Channel operations),你就可以马上跳出来。

如果你使用一个标志,你必须等待阻塞操作完成,然后你可以检查你的标志。在某些情况下,无论如何你都必须这样做,比如使用标准的不可中断的InputStream/OutputStream。

在这种情况下,当线程中断时,它不会中断IO,但是,您可以在代码中轻松地常规执行此操作(并且应该在可以安全停止和清理的战略点执行此操作)。

if (Thread.currentThread().isInterrupted()) {
  // cleanup and stop execution
  // for example a break in a loop
}

就像我说的,Thread.interrupt()的主要优点是可以立即中断可中断调用,这是用flag方法做不到的。

简单的回答是: 你可以用以下两种方法之一在内部停止线程:

run方法命中返回子例程。 运行方法完成,并隐式返回。

你也可以从外部停止线程:

电话系统。退出(这会终止整个进程) 调用线程对象的interrupt()方法* 看看线程是否有一个听起来可行的方法(如kill()或stop())

*:期望是这应该停止一个线程。然而,当这种情况发生时,线程实际做什么完全取决于开发人员在创建线程实现时编写的内容。

你在run方法实现中看到的一个常见模式是while(boolean){},其中布尔值通常是名为isRunning的东西,它是其线程类的成员变量,它是volatile的,通常由其他线程通过sort setter方法访问,例如kill() {isRunnable=false;}。这些子例程很好,因为它们允许线程在终止之前释放它所持有的任何资源。