Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中正确停止线程?_Java_Multithreading_Listener - Fatal编程技术网

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

如何在Java中正确停止线程?,java,multithreading,listener,Java,Multithreading,Listener,我需要一个解决方案来正确停止Java中的线程 我有IndexProcessor类,它实现了可运行接口: public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); @Override public void run() { boolean run

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

我有
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)
我正在使用JDK1.6。因此,问题是:

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


p.S.我不想使用
.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.");
        }
    }
}

您应该始终通过检查
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;
    }
}
        ...
        try {
            LOGGER.debug("Sleeping...");
            for (int i = 0; (i < 150) && this.execute; i++) {
                Thread.sleep((long) 100);
            }

            LOGGER.debug("Processing");
        } catch (InterruptedException e) {
        ...
然后可以通过调用
thread.stopExecuting()
来结束线程。这样线程结束时是干净的,但这需要15秒(由于您的睡眠)。 如果真的很紧急,您仍然可以调用thread.interrupt(),但首选的方法应该始终是检查标志

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

    }
}
为了避免等待15秒,您可以按如下方式分割睡眠:

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;
    }
}
        ...
        try {
            LOGGER.debug("Sleeping...");
            for (int i = 0; (i < 150) && this.execute; i++) {
                Thread.sleep((long) 100);
            }

            LOGGER.debug("Processing");
        } catch (InterruptedException e) {
        ...
。。。
试一试{
调试(“睡眠…”);
对于(inti=0;(i<150)和&this.execute;i++){
线程。睡眠((长)100);
}
调试(“处理”);
}捕捉(中断异常e){
...

对于同步线程,我更喜欢使用
CountDownLatch
,这有助于线程等待进程完成。在这种情况下,worker类使用一个具有给定计数的
CountDownLatch
实例进行设置。调用
wait
方法将被阻止,直到当前计数因错误而达到零为止已达到
倒计时
方法的职业或超时设置。此方法允许立即中断线程,而无需等待指定的等待时间:

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
上执行倒计时,并
将该线程连接到主线程:

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

使用
Thread.interrupt()
是一种完全可以接受的方法。事实上,它可能比上面建议的标志更可取。原因是如果您处于可中断的阻塞调用中(如
Thread.sleep
或使用java.nio通道操作),你实际上可以马上摆脱这些

如果使用标志,则必须等待阻塞操作完成,然后才能检查标志。在某些情况下,必须执行此操作,例如使用不可中断的标准
InputStream
/
OutputStream

在这种情况下,当一个线程被中断时,它不会中断IO,但是,您可以在代码中很容易地例行地执行此操作(您应该在可以安全停止和清理的战略点执行此操作)

正如我所说,
Thread.interrupt()
的主要优点是,您可以立即中断可中断的调用,这是使用flag方法无法做到的。

简单回答: 您可以通过以下两种常见方式之一在内部停止线程:

  • run方法命中一个返回子例程
  • Run方法完成,并隐式返回
您还可以从外部停止线程:

  • 调用
    system.exit
    (这会终止整个过程)
  • 调用线程对象的
    interrupt()
    方法*
  • 查看线程是否有一个听起来可以工作的实现方法(如
    kill()
    stop()
*:我们的期望是,这应该会停止线程。但是,当这种情况发生时,线程实际做什么完全取决于开发人员在创建线程实现时编写的内容


运行方法实现中常见的模式是
while(boolean){}
,其中boolean通常是名为
isRunning
,它是线程类的一个成员变量,是易失性的,并且通常可由其他线程通过某种setter方法访问,例如
kill(){isRunnable=false;}
。这些子例程很好,因为它们允许线程在终止之前释放它所持有的任何资源。

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

私有易失性线程闪烁器;
公共停车场(){
闪烁=零;
}
公开募捐{
Thread thisThread=Thread.currentThread();
while(闪烁=此线程){
试一试{
睡眠(间隔);
}捕捉(中断异常e){
}
重新油漆();
}
}
对于等待时间较长的线程(例如,等待输入),请使用
thread.interrupt

公共作废停止(){
线程垂死=服务员;
服务员=空;
垂死的中断();
}

我没有在安卓系统中使用中断,所以我使用了这种方法,效果非常好:

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

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


ref-

中途终止线程总是会生成异常。如果这是正常行为,那么您可以捕获并忽略
中断异常
。这就是我的想法,但我也不知道标准方法是什么。我还没有看到