Обработка исключения InterruptedException в Java
В чем разница между следующими способами обработки InterruptedException
? Какой способ сделать это лучше всего?
try {
// ...
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
...или:
try {
//...
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Я хотел бы также знать, в каких сценариях используются эти два.
Переведено автоматически
Ответ 1
В чем разница между следующими способами обработки InterruptedException? Какой способ сделать это лучше всего?
Вы, вероятно, задались этим вопросом, потому что вы вызвали метод, который выдает ошибку InterruptedException
.
Прежде всего, вы должны увидеть, throws InterruptedException
что это такое: часть сигнатуры метода и возможный результат вызова метода, который вы вызываете. Итак, начните с принятия того факта, что InterruptedException
является абсолютно допустимым результатом вызова метода.
Теперь, если вызываемый вами метод выдает такое исключение, что должен делать ваш метод? Вы можете найти ответ, подумав о следующем:
Имеет ли смысл для метода , который вы реализуете , выдавать InterruptedException
? Иными словами, является ли это InterruptedException
разумным результатом при вызове вашего метода?
Если да, то это
throws InterruptedException
должно быть частью сигнатуры вашего метода, и вы должны позволить исключению распространяться (т. Е. Вообще не перехватывать его).Пример: ваш метод ожидает значения из сети, чтобы завершить вычисление и вернуть результат. Если блокирующий сетевой вызов выдает
InterruptedException
ваш метод не может завершить вычисление обычным способом. Вы позволяетеInterruptedException
распространяться.int computeSum(Server server) throws InterruptedException {
// Any InterruptedException thrown below is propagated
int a = server.getValueA();
int b = server.getValueB();
return a + b;
}Если нет, то вам не следует объявлять свой метод с помощью
throws InterruptedException
и вы должны (обязаны!) перехватить исключение. Теперь в этой ситуации важно помнить о двух вещах:Кто-то прервал ваш поток. Этот кто-то, вероятно, хочет отменить операцию, завершить программу изящно или что-то еще. Вы должны быть вежливы с этим кем-то и вернуться к своему методу без дальнейших церемоний.
Даже несмотря на то, что ваш метод может выдавать разумное возвращаемое значение в случае
InterruptedException
того факта, что поток был прерван, все еще может иметь значение. В частности, код, вызывающий ваш метод, может быть заинтересован в том, произошло ли прерывание во время выполнения вашего метода. Поэтому вам следует зарегистрировать факт прерывания, установив флаг interrupted:Thread.currentThread().interrupt()
Пример: Пользователь попросил напечатать сумму двух значений. Вывод "
Failed to compute sum
" допустим, если сумму невозможно вычислить (и это намного лучше, чем допустить сбой программы с трассировкой стека из-заInterruptedException
). Другими словами, не имеет смысла объявлять этот метод с помощьюthrows InterruptedException
.void printSum(Server server) {
try {
int sum = computeSum(server);
System.out.println("Sum: " + sum);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // set interrupt flag
System.out.println("Failed to compute sum");
}
}
К настоящему моменту должно быть ясно, что просто выполнять throw new RuntimeException(e)
- плохая идея. Это не очень вежливо по отношению к вызывающему. Вы могли бы создать новое исключение во время выполнения, но основная причина (кто-то хочет, чтобы поток остановил выполнение) может быть потеряна.
Другие примеры:
Реализация
Runnable
: Как вы, возможно, обнаружили, сигнатураRunnable.run
не допускает повторного перемещенияInterruptedExceptions
. Что ж, вы подписались на реализациюRunnable
, что означает, что вы подписались на работу с возможнымиInterruptedExceptions
. Либо выберите другой интерфейс, напримерCallable
, либо следуйте второму подходу, описанному выше.
Вызов
Thread.sleep
: вы пытаетесь прочитать файл, и в спецификации сказано, что вы должны попробовать 10 раз с интервалом в 1 секунду. Вы вызываетеThread.sleep(1000)
. Итак, вам нужно разобраться сInterruptedException
. Для такого метода, какtryToReadFile
имеет смысл сказать, "Если меня прервут, я не смогу завершить свое действие по попытке прочитать файл". Другими словами, для метода имеет смысл выдавать ошибкуInterruptedExceptions
.
String tryToReadFile(File f) throws InterruptedException {
for (int i = 0; i < 10; i++) {
if (f.exists())
return readFile(f);
Thread.sleep(1000);
}
return null;
}
Этот пост был переписан как статья здесь.
Ответ 2
Так получилось, что я просто читал об этом сегодня утром по дороге на работу в Параллелизм Java на практике Брайана Гетца. По сути, он говорит, что вы должны сделать одну из трех вещей
Распространите
InterruptedException
- Объявите свой метод для выдачи checkedInterruptedException
, чтобы вызывающий объект имел с ним дело.Восстановить прерывание - иногда вы не можете выполнить выборку
InterruptedException
. В этих случаях вы должны перехватитьInterruptedException
и восстановить состояние прерывания, вызвавinterrupt()
метод вcurrentThread
, чтобы код, находящийся выше по стеку вызовов, мог увидеть, что было выдано прерывание, и быстро вернуться из метода. Примечание: это применимо только тогда, когда ваш метод имеет семантику "try" или "best effort", т. е. Ничего критичного не произойдет, если метод не достигнет своей цели. Например,log()
orsendMetric()
может быть таким методом, илиboolean tryTransferMoney()
, но неvoid transferMoney()
. Смотрите Здесь для получения более подробной информации.- Игнорируйте прерывание внутри метода, но восстанавливайте состояние при выходе - например, через Guava
Uninterruptibles
.Uninterruptibles
используйте шаблонный код, как в примере с не подлежащей отмене задачей в JCIP § 7.1.3.
Ответ 3
Что вы пытаетесь сделать?
InterruptedException
Выдается, когда поток ожидает или переходит в спящий режим, а другой поток прерывает его, используя interrupt
метод в классе Thread
. Итак, если вы перехватываете это исключение, это означает, что поток был прерван. Обычно нет смысла вызывать Thread.currentThread().interrupt();
повторно, если вы не хотите проверить состояние потока "прервано" откуда-то еще.
Что касается вашего другого варианта выдачи RuntimeException
, это не кажется очень разумным поступком (кто это поймает? как это будет обработано?) но трудно сказать больше без дополнительной информации.
Ответ 4
Правильный выбор по умолчанию - добавить исключение InterruptedException в список throws . Прерывание указывает на то, что другой поток желает, чтобы ваш поток завершился. Причина этого запроса неочевидна и полностью зависит от контекста, поэтому, если у вас нет каких-либо дополнительных знаний, вы должны предположить, что это просто дружественное завершение работы, и все, что позволяет избежать этого завершения работы, является недружественным ответом.
Java не будет случайным образом выдавать InterruptedException, все рекомендации не повлияют на ваше приложение, но я столкнулся со случаем, когда использование разработчиком стратегии "проглотить" стало очень неудобным. Команда разработала большой набор тестов и использовала Thread.Много спим. Теперь мы начали запускать тесты на нашем сервере CI, и иногда из-за дефектов в коде зависали в постоянном ожидании. Чтобы усугубить ситуацию, при попытке отменить задание CI оно так и не закрылось, потому что поток.Прерывание, которое должно было прервать тест, не прервало задание. Нам пришлось войти в систему и вручную отключить процессы.
Короче говоря, если вы просто создаете InterruptedException, вы соответствуете намерению по умолчанию, согласно которому ваш поток должен завершиться. Если вы не можете добавить InterruptedException в свой список бросков, я бы включил его в RuntimeException .
Можно привести очень рациональный аргумент, что InterruptedException должно быть самим RuntimeException, поскольку это способствовало бы лучшей обработке "по умолчанию". Это не исключение RuntimeException только потому, что разработчики придерживались категорического правила, согласно которому исключение RuntimeException должно представлять ошибку в вашем коде. Поскольку исключение InterruptedException не возникает непосредственно из-за ошибки в вашем коде, это не так. Но реальность такова, что часто исключение InterruptedException возникает из-за ошибки в вашем коде (т. Е. Бесконечного цикла, мертвой блокировки), и прерывание - это метод какого-либо другого потока для устранения этой ошибки.
Если вы знаете, что необходимо выполнить рациональную очистку, тогда сделайте это. Если вы знаете более глубокую причину прерывания, вы можете приступить к более комплексной обработке.
Итак, вкратце, ваши варианты обработки должны следовать этому списку:
- По умолчанию add to throws .
- Если не разрешено добавлять в throws, создайте исключение RuntimeException (e). (Лучший выбор из нескольких ошибочных вариантов)
- Только когда вы знаете явную причину прерывания, обрабатывайте его по своему усмотрению. Если обработка локальна для вашего метода, сбросьте прерывание вызовом Thread.currentThread().interrupt() .