Как остановить поток Java изящно? - PullRequest
65 голосов
/ 07 июля 2010

Я написал поток, выполнение которого занимает слишком много времени, и кажется, что оно не выполнено полностью. Я хочу остановить поток изящно. Любая помощь?

Ответы [ 6 ]

68 голосов
/ 07 июля 2010

Хороший способ состоит в том, чтобы run() Нити защищался переменной boolean и устанавливал его на true извне, когда вы хотите остановить его, что-токак:

class MyThread extends Thread
{
  volatile boolean finished = false;

  public void stopMe()
  {
    finished = true;
  }

  public void run()
  {
    while (!finished)
    {
      //do dirty work
    }
  }
}

Когда-то существовал метод stop(), но, как указано в документации

Этот метод небезопасен.Остановка потока с помощью Thread.stop приводит к тому, что он разблокирует все заблокированные мониторы (как естественное следствие непроверенного исключения ThreadDeath, распространяющегося вверх по стеку).Если какой-либо из объектов, ранее защищенных этими мониторами, находился в несовместимом состоянии, поврежденные объекты становятся видимыми для других потоков, что может привести к произвольному поведению.

Вот почему у вас должен быть охранник ..

12 голосов
/ 07 июля 2010

Недостаток использования флага для остановки вашего потока состоит в том, что если поток ожидает или спит, вам придется ждать, пока он завершит ожидание / спящий режим. Если вы вызываете метод прерывания в потоке, это приведет к тому, что вызов ожидания или ожидания будет завершен с InterruptedException.

(Вторая плохая часть подхода с использованием флагов заключается в том, что в большинстве нетривиальных кодов используются библиотеки, такие как java.util.concurrent, где классы специально предназначены для использования прерывания для отмены. Попытка использовать флаг, свернутый вручную, в задача, переданная Исполнителю, будет неудобной.)

Вызов interrupt () также устанавливает свойство прерывания, которое вы можете использовать в качестве флага, чтобы проверить, выходить ли (если поток не ожидает или не спит).

Вы можете написать метод run потока, чтобы InterruptedException перехватывался вне какой-либо логики цикла, которую выполняет поток, или вы можете перехватить исключение в цикле и близко к вызову, вызвавшему исключение, установив флаг прерывания внутри catch заблокировать исключение InterruptedException, чтобы поток не потерял отслеживание того, что он был прерван. Прерванный поток все еще может сохранять контроль и завершать обработку на своих собственных условиях.

Скажем, я хочу написать рабочий поток, который работает с приращениями, где по какой-то причине находится спящий режим, и я не хочу выходить из спящего режима, чтобы завершить обработку без выполнения оставшейся работы для этого приращения, Я только хочу, чтобы это вышло, если это между приращениями:

class MyThread extends Thread
{
    public void run()
    {
        while (!Thread.currentThread().isInterrupted())
        {
            doFirstPartOfIncrement();
            try {
                Thread.sleep(10000L);
            } catch (InterruptedException e) {
                // restore interrupt flag
                Thread.currentThread().interrupt();
            }
            doSecondPartOfIncrement();
        }
    }
}

Вот ответ на аналогичный вопрос, включая пример кода.

10 голосов
/ 07 июля 2010

Вы не должны убивать Thread из другого.Это считается довольно плохой привычкой.Однако есть много способов.Вы можете использовать оператор return из потока run метода.Или вы можете проверить, был ли поток уже прерван, и тогда он отменит свою работу.Fe:

while (!isInterrupted()) { 
  // doStuff
}
4 голосов
/ 07 июля 2010

Создайте летучее логическое значение stop где-нибудь.Затем в коде, который выполняется в потоке, регулярно делайте

 if (stop) // end gracefully by breaking out of loop or whatever

Чтобы остановить поток, установите stop в true.

Я думаю, вы должны сделать это вручную таким образом,В конце концов, только код, выполняющийся в потоке, имеет представление о том, что является изящным, а что нет.

3 голосов
/ 07 июля 2010

Вам необходимо отправить стоп-сообщение в поток, а сам поток должен принять меры, если сообщение было получено.Это довольно легко, если длительное действие выполняется внутри цикла:

public class StoppableThread extends Thread {

   private volatile boolean stop = false;

   public void stopGracefully() {
     stop = true;
   }

   public void run() {
     boolean finished = false;
     while (!stop && !finished) {
       // long running action - finished will be true once work is done
     }
   }
}
1 голос
/ 09 июня 2016

Чтобы остановить поток, никто, кажется, не упомянул (неправильно), используя исключение:

abstract class SelfStoppingThread extends Thread {

    @Override
    public final void run() {
        try {
            doRun();
        } catch (final Stop stop) {
            //optional logging
        }
    }

    abstract void doRun();

    protected final void stopSelf() {
        throw new Stop();
    }

    private static final class Stop extends RuntimeException {};

}

Подкласс должен просто переопределить doRun (), как обычно, с потоком, и вызывать stopSelf () всякий раз, когда он чувствует, что хочет остановиться. IMO кажется чище, чем использование флага в цикле while.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...