Краткая версия: этот код неверен и вызовет бесконечный цикл (у меня все еще есть сомнения, но может зависеть от реализаций JVM). Настройка статуса прерывания - это правильная вещь, но она должна выйти из цикла, в конечном счете, проверить тот же статус прерывания с помощью Thread.isInterrupted().
Длинная версия для случайного читателя:
Проблема состоит в том, чтобы остановить поток, который в настоящее время выполняется некоторая работа, в ответ на кнопку «Отмена» от пользователя или из-за какой-либо другой логики приложения.
Первоначально Java поддерживал метод «stop», который превентивно остановил поток. Этот метод был продемонстрирован как небезопасный, потому что не дал остановленному потоку какой-либо (простой) способ очистки, освободить ресурсы, не подвергать частично модифицированным объектам и т. Д.
Итак, Java превратилась в «совместную» систему «прерывания потока». Эта система довольно проста: поток работает, кто-то называет это «прерывание», флаг установлен в потоке, ответственность за Thread - проверить, была ли она прервана или нет, и действовать соответствующим образом.
Таким образом, правильный Thread.run (или Runnable.run, ГОО и т.д ..) реализация метода должно быть что-то вроде:
public void run() {
while (!Thread.getCurrentThread().isInterrupted()) {
// Do your work here
// Eventually check isInterrupted again before long running computations
}
// clean up and return
}
Это прекрасно, пока весь код ваш поток выполняемым является внутри вашего метода запуска, и вы никогда не называете то, что блокирует в течение длительного времени ... что часто бывает не так, потому что если вы создаете Thread, это потому, что у вас есть что-то долгое.
Простейший метод, который блокирует Thread.sleep (millis), на самом деле это единственное, что он делает: он блокирует поток за заданное количество времени.
Теперь, если прерывание поступает, когда ваш поток находится внутри Thread.sleep (600000000), без какой-либо другой поддержки потребуется много для того, чтобы он добрался до точки, где он проверяет isInterrupted.
Есть ситуации, когда ваша нить никогда не выйдет. Например, ваш поток что-то вычисляет и отправляет результаты в BlockingQueue с ограниченным размером, вы вызываете queue.put (myresult), он будет блокировать до тех пор, пока потребитель не освободит место в очереди, если в то же время потребитель был прервано (или умерло или что-то еще), это пространство никогда не прибудет, метод не вернется, проверка. IsInterrupted никогда не будет выполнена, ваш поток застрял.
Чтобы избежать этой ситуации, все (большинство) методов, которые прерывают поток (должны), бросают InterruptedException. Это исключение просто говорит вам: «Я ждал этого и этого, но в то же время поток как прерванный, вы должны сделать очистку и выйти как можно скорее».
Как и все исключения, если вы не знаете, что делать, вы должны повторно бросить его и надеяться, что кто-то из вас в стеке вызовов знает.
ПрерываниеЭксперименты еще хуже, поскольку, когда они выбрасываются, «прерванный статус» очищается. Это означает, что просто ловить и их игнорирование приведет к потоку, который обычно не останавливается:
public void run() {
while (!Thread.getCurrentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Nothing here
}
}
}
В этом примере, если приходит прерывание во время метода сна() (который +99,9999999999% времени), он будет вызывать InterruptedException, очистить флаг прерывания, тогда цикл будет продолжаться, так как флаг прерывания является ложным, и поток не останавливается.
Именно поэтому, если вы правильно реализуете свое «пока», используя .isInterrupted, и вам действительно нужно поймать InterruptedException, и у вас нет ничего особенного (например, очистка, возврат и т. Д.), Чтобы сделать с ним меньше что вы можете сделать, это снова установить флаг прерывания.
Проблема в коде, который вы опубликовали, заключается в том, что «while» полагается исключительно на mExited, чтобы решить, когда остановиться, а не ALSO on isInterrupted.
while (!mExited && !Thread.getCurrentThread().isInterrupted()) {
Или он может выйти, когда прервала:
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return; // supposing there is no cleanup or other stuff to be done
}
Установка флага isInterrupted назад к истине также важно, если вы не контролируете тему.Например, если вы находитесь в runnable, который выполняется в пуле потоков какого-либо типа или внутри любого метода в любом месте, где вы не владеете и не управляете потоком (простой случай: сервлет), вы не знаете, если прерывание для «вас» (в случае с сервлетом клиент закрыл соединение, и контейнер пытается остановить вас, чтобы освободить поток для других запросов) или если он нацелен на поток (или систему) в целом (контейнер закрывается, останавливая все).
В этой ситуации (что составляет 99% от кода), если вы не можете повторить исключение InterruptedException (которое, к сожалению, проверено), единственный способ распространить стек на пул потоков, в котором поток был прерван , перед возвратом вернет флаг true.
Таким образом, он будет распространяться по стеку, в результате генерируя больше InterruptedException, вплоть до владельца потока (будь то сам jvm, Исполнителя или любой другой пул потоков), который может реагировать должным образом (повторно использовать поток, let it die, System.exit (1) ...)
Большая часть этого раскрывается в главе 7 Java Concurrency in Practice, очень хорошей книге, которую я рекомендую всем, кто интересуется компьютерным программированием в целом, а не только Java, потому что проблемы и решения аналогичны во многих других средах, и объяснения очень хорошо написаны.
Почему Sun решила сделать InterruptedException проверенной, когда в большинстве документов предлагается безжалостно реконструировать ее, и почему они решили очистить прерванный флаг, когда выбрасывают это исключение, когда правильная вещь - снова установить его в true время остается открытым для обсуждения.
Однако, если .wait освобождает блокировку ПЕРЕД проверкой флага прерывания, он открывает небольшую дверь из другого потока, чтобы изменить mExited boolean. К сожалению, метод wait() является родным, поэтому источник этой конкретной JVM должен быть проверен. Это не меняет того факта, что код, который вы опубликовали, плохо кодируется.
Возможный дубликат [Обработка InterruptedException в Java] (http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java), и этот тоже: http://stackoverflow.com/questions/5915156/ how-can-i-kill-a-thread-without-use-stop/5915306 # 5915306 – assylias
Это не имеет никакого отношения к первой теме, которую вы связали (кроме того, что оба связаны с InterruptedException). Это более сложная проблема, и вопрос полностью другой. (Я знаю, что вы написали «возможно», поэтому я просто говорю всем, что это определенно не так). Второй, добавленный после редактирования комментария, более сложен, он точно подтверждает мое подозрение, что вышеуказанный код неверен, но так как это официальный Android-источник, явный ответ от эксперта будет приятным. –
Вы правы - на том факте, что это не дубликат, и на том факте, что сброс прерванного состояния немедленно заставит ждать выхода. У кого-то может быть хорошая причина для этого ... – assylias