Как правильно остановить Thread в 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. Итак, вопрос:

Как я могу остановить поток и не исключать какие-либо исключения?

P.S. Я не хочу использовать метод .stop();, потому что он устарел.

Ответ 1

В классе 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.");
        }
    }
}

Ответ 2

Использование 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() является то, что вы можете немедленно вырваться из прерывистых вызовов, которые вы не можете сделать с использованием флага.

Ответ 3

Простой ответ: Вы можете остановить поток INTERNATIONAL одним из двух общих способов:

  • Метод run обращается к подпрограмме return.
  • Выполнить метод завершается и возвращается неявно.

Вы также можете остановить потоки EXTERNALLY:

  • Вызов system.exit (это убивает весь процесс)
  • Вызвать метод потока interrupt() метод *
  • Посмотрите, есть ли в потоке реализованный метод, который звучит так, как будто он работает (например, kill() или stop())

*: Ожидается, что это должно остановить поток. Однако то, что поток фактически делает, когда это происходит, полностью зависит от того, что написал разработчик, когда они создали реализацию потока.

Общим шаблоном, который вы видите с помощью реализации метода выполнения, является while(boolean){}, где логическое значение обычно имеет имя с именем isRunning, оно является переменной-членом его класса потоков, оно изменчиво и обычно доступно другими потоками установщиком метод сортировки, например kill() { isRunnable=false; }. Эти подпрограммы хороши, потому что они позволяют потоку освобождать любые ресурсы, которые он проводит до завершения.

Ответ 4

Вы всегда должны заканчивать потоки, проверяя флаг в цикле 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) {
        ...

Ответ 5

Для синхронизации потоков я предпочитаю использовать CountDownLatch, который помогает потокам ждать завершения процесса. В этом случае рабочий класс настраивается с экземпляром 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;
        }

    }
}

Если вы хотите закончить выполнение другого потока, выполните countDown в CountDownLatch и 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.");
        } 
    }
}

Ответ 6

Дополнительная информация. Оба флага и прерывания предлагаются в документе 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();
 }

Ответ 7

Я не получил прерывание для работы на 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;
 }

Ответ 8

Если мы используем JDK 1.0, мы можем вызвать метод устаревших методов Thread(), чтобы завершить его. Использование stop() невероятно опасно, так как оно убьет ваш поток, даже если он находится в середине чего-то важного. Невозможно защитить себя, поэтому, если вы обнаружите код, который использует stop(), вы должны нахмуриться.

Как мы закрываем поток чисто?

В Java начало потоков легко, но их закрытие требует большого внимания и усилий.

Вот как он разработан на Java. В каждом потоке 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();

source: Как остановить поток в java | MultiThreading

Ответ 9

Как правило, поток прерывается, когда он прерывается. Итак, почему бы не использовать родной логическое значение? Попробуйте 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();

ref- Как убить тему? без использования stop();

Ответ 10

Когда-нибудь я попробую 1000 раз в моем onDestroy()

      @Override
    protected void onDestroy() {
        boolean retry = true;
        int counter = 0;
        while(retry && counter<1000)
        {
            counter++;
            try{thread.setRunnung(false);
                thread.join();
                retry = false;
                thread = null; //garbage can coll
            }catch(InterruptedException e){e.printStackTrace();}
        }

    }