В чем разница между следующими способами обработки InterruptedException
? Каков наилучший способ сделать это?
try{
//...
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
ИЛИ
try{
//...
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
РЕДАКТИРОВАТЬ: Я хотел бы также знать, в каких сценариях используются эти два.
Ответы:
Вы, вероятно, пришли, чтобы задать этот вопрос, потому что вы вызвали метод, который выбрасывает
InterruptedException
.Прежде всего вы должны увидеть,
throws InterruptedException
что это такое: часть сигнатуры метода и возможный результат вызова метода, который вы вызываете. Итак, начнем с того, что следует признать тот факт, что anInterruptedException
является вполне допустимым результатом вызова метода.Теперь, если вызываемый вами метод выдает такое исключение, что должен делать ваш метод? Вы можете выяснить ответ, подумав о следующем:
Имеет ли смысл использовать метод, который вы реализуете
InterruptedException
? Иными словами,InterruptedException
это разумный результат при вызове вашего метода?Если да , то
throws InterruptedException
должно быть частью сигнатуры вашего метода, и вы должны позволить распространению исключения (то есть вообще не перехватывать его).Если нет , то вы не должны объявлять свой метод с помощью,
throws InterruptedException
и вы должны (должны!) Перехватить исключение. В этой ситуации важно помнить две вещи:Кто-то прервал вашу тему. Что кто-то, вероятно, хочет отменить операцию, завершить программу изящно или что-то еще. Вы должны быть вежливы с этим человеком и возвращаться к своему методу без лишних слов.
Даже несмотря на то, что ваш метод может создать разумное возвращаемое значение в случае
InterruptedException
, если поток был прерван, все равно может иметь значение. В частности, код, который вызывает ваш метод, может быть заинтересован в том, произошло ли прерывание во время выполнения вашего метода. Поэтому вы должны зарегистрировать факт прерывания, установив флаг прерывания:Thread.currentThread().interrupt()
К настоящему времени должно быть ясно, что просто делать
throw new RuntimeException(e)
это плохая идея. Это не очень вежливо к звонящему. Вы можете изобрести новое исключение во время выполнения, но основная причина (кто-то хочет, чтобы поток остановил выполнение) могла быть потеряна.Другие примеры:
Этот пост был переписан как статья здесь .
источник
interrupt()
чтобы сохранить прерванный статус. В чем причина не делать это наThread.sleep()
?Thread.currentThread.interrupt()
вInterruptedException
блоке перехвата будет только устанавливать флаг прерывания. Это не заставит другогоInterruptedException
быть брошенным / пойманным во внешнемInterruptedException
блоке захвата.Как это случилось, я только что читал об этом сегодня утром по пути на работу в Java Concurrency In Practice от Брайана Гетца. В основном он говорит, что вы должны сделать одну из трех вещей
Распространите
InterruptedException
- Объявите свой метод, чтобы бросить проверенныйInterruptedException
так, чтобы ваш вызывающий имел дело с этим.Восстановить прерывание - иногда вы не можете бросить
InterruptedException
. В этих случаях вы должны перехватитьInterruptedException
и восстановить состояние прерывания, вызвавinterrupt()
метод,currentThread
чтобы код, находящийся выше стека вызовов, мог видеть, что было выдано прерывание, и быстро вернуться из метода. Примечание: это применимо только тогда, когда ваш метод имеет семантику «попробуй» или «наилучшее усилие», то есть ничего критического не произойдет, если метод не достигнет своей цели. Например,log()
илиsendMetric()
может быть такой метод, илиboolean tryTransferMoney()
, но нетvoid transferMoney()
. Смотрите здесь для более подробной информации.Uninterruptibles
.Uninterruptibles
Возьмите шаблон кода, как в примере «Невозможная задача» в JCIP § 7.1.3.источник
Что ты пытаешься сделать?
InterruptedException
Выбрасывается , когда поток ожидает или спать , а другой поток прерывает его , используяinterrupt
метод в классеThread
. Поэтому, если вы поймаете это исключение, это означает, что поток был прерван. Обычно нет смыслаThread.currentThread().interrupt();
снова звонить , если только вы не хотите проверить «прерванный» статус потока откуда-то еще.Что касается вашего другого варианта броска
RuntimeException
, это не кажется очень мудрым делом (кто поймает это? Как это будет обработано?), Но трудно сказать больше без дополнительной информации.источник
Thread.currentThread().interrupt()
устанавливает флаг прерывания (снова), что действительно полезно, если мы хотим, чтобы прерывание было замечено и обработано на более высоком уровне.Для меня главное в этом: InterruptedException - это не ошибка, это поток, выполняющий то, что вы ему сказали. Поэтому перебрасывание его в оболочку RuntimeException не имеет смысла.
Во многих случаях имеет смысл перебрасывать исключение, заключенное в исключение RuntimeException, когда вы говорите: я не знаю, что здесь пошло не так, и не могу ничего сделать, чтобы это исправить, я просто хочу, чтобы оно вышло из текущего потока обработки и ударил любой обработчик исключений для всего приложения, который у меня есть, чтобы он мог его записать. Это не относится к InterruptedException, это просто поток, отвечающий на вызов interrupt () для него, он выдает InterruptedException, чтобы помочь своевременно отменить обработку потока.
Поэтому распространяйте InterruptedException или используйте его разумно (имея в виду место, где он должен был выполнить то, что предполагалось) и сбросьте флаг прерывания. Обратите внимание, что флаг прерывания очищается при возникновении исключения InterruptedException; разработчики библиотеки Jdk предполагают, что перехват исключения сводится к его обработке, поэтому по умолчанию флаг очищается.
Так что, безусловно, первый способ лучше, второй опубликованный пример в вопросе бесполезен, если только вы не ожидаете, что поток действительно прервется, и прерывание приведет к ошибке.
Вот ответ, который я написал, описывающий, как работают прерывания, с примером . Вы можете увидеть в примере кода, где он использует InterruptedException для выхода из цикла while в методе run Runnable.
источник
Правильный выбор по умолчанию - добавить InterruptedException в ваш список бросков. Прерывание указывает, что другой поток желает, чтобы ваш поток завершился. Причина этого запроса не очевидна и носит полностью контекстуальный характер, поэтому, если у вас нет дополнительных знаний, вы должны предполагать, что это просто дружественное завершение работы, а все, что позволяет избежать такого выключения, является неприемлемым ответом.
Java не будет случайным образом выбрасывать InterruptedException, все советы не будут влиять на ваше приложение, но я столкнулся со случаем, когда следование стратегии «ласточки» разработчика стало очень неудобным. Команда разработала большой набор тестов и много использовала Thread.Sleep. Теперь мы начали запускать тесты на нашем CI-сервере, и иногда из-за дефектов в коде застревали в постоянном ожидании. Что еще хуже, при попытке отменить задание CI оно никогда не закрывалось, поскольку прерывание Thread.Interrupt, предназначенное для прерывания теста, не прерывало задание. Нам пришлось войти в ящик и вручную убить процессы.
Короче говоря, если вы просто выбросите InterruptedException, вы соответствуете намерению по умолчанию, что ваш поток должен завершиться. Если вы не можете добавить InterruptedException в свой список бросков, я поместил бы его в RuntimeException.
Существует очень рациональный аргумент в пользу того, что InterruptedException должен быть самим RuntimeException, поскольку это будет способствовать лучшей обработке по умолчанию. Это не RuntimeException только потому, что разработчики придерживались категориального правила, согласно которому RuntimeException должно представлять ошибку в вашем коде. Поскольку InterruptedException не возникает непосредственно из-за ошибки в вашем коде, это не так. Но реальность такова, что часто возникает InterruptedException, потому что в вашем коде есть ошибка (т. Е. Бесконечный цикл, тупик), а Interrupt - это метод какого-то другого потока для обработки этой ошибки.
Если вы знаете, что есть рациональная очистка, то сделайте это. Если вы знаете более глубокую причину прерывания, вы можете взять на себя более полную обработку.
Итак, в итоге ваш выбор для обработки должен следовать этому списку:
источник
Я просто хотел добавить одну последнюю опцию к тому, что упоминают большинство людей и статьи. Как заявил mR_fr0g, важно правильно обрабатывать прерывание одним из следующих способов:
Распространение InterruptException
Восстановить состояние прерывания в потоке
Или дополнительно:
Нет ничего плохого в обработке прерывания в соответствии с вашими обстоятельствами. Поскольку прерывание является запросом на завершение, в отличие от принудительной команды, вполне допустимо завершить дополнительную работу, чтобы приложение могло корректно обработать запрос. Например, если поток находится в режиме ожидания, ожидает ввода-вывода или аппаратного ответа, когда он получает прерывание, то вполне корректно корректно закрыть все соединения перед завершением потока.
Я настоятельно рекомендую разобраться в теме, но эта статья является хорошим источником информации: http://www.ibm.com/developerworks/java/library/j-jtp05236/
источник
Я бы сказал, что в некоторых случаях ничего не поделаешь. Возможно, это не то, что вы должны делать по умолчанию, но в случае, если не должно быть никакого способа для прерывания, я не уверен, что еще делать (возможно, ошибка регистрации, но это не влияет на ход программы).
Один случай - случай, когда у вас есть очередь задач (блокировка). Если у вас есть Поток демона, выполняющий эти задачи, и вы не прерываете Поток самостоятельно (насколько мне известно, jvm не прерывает потоки демона при завершении работы jvm), я не вижу возможности для прерывания, и поэтому это может быть просто игнорируется. (Я знаю, что поток демонов может быть убит jvm в любое время и, следовательно, в некоторых случаях не подходит).
РЕДАКТИРОВАТЬ: другой случай может быть защищенных блоков, по крайней мере, на основе учебника Oracle по адресу: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
источник