Вопрос-ответ

Handling InterruptedException in Java

Обработка исключения 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 и вы должны (обязаны!) перехватить исключение. Теперь в этой ситуации важно помнить о двух вещах:



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


    2. Даже несмотря на то, что ваш метод может выдавать разумное возвращаемое значение в случае 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 на практике Брайана Гетца. По сути, он говорит, что вы должны сделать одну из трех вещей


  1. Распространите InterruptedException - Объявите свой метод для выдачи checked InterruptedException, чтобы вызывающий объект имел с ним дело.


  2. Восстановить прерывание - иногда вы не можете выполнить выборку InterruptedException. В этих случаях вы должны перехватить InterruptedException и восстановить состояние прерывания, вызвав interrupt() метод в currentThread, чтобы код, находящийся выше по стеку вызовов, мог увидеть, что было выдано прерывание, и быстро вернуться из метода. Примечание: это применимо только тогда, когда ваш метод имеет семантику "try" или "best effort", т. е. Ничего критичного не произойдет, если метод не достигнет своей цели. Например, log() or sendMetric() может быть таким методом, или boolean tryTransferMoney(), но не void transferMoney(). Смотрите Здесь для получения более подробной информации.


  3. Игнорируйте прерывание внутри метода, но восстанавливайте состояние при выходе - например, через 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 возникает из-за ошибки в вашем коде (т. Е. Бесконечного цикла, мертвой блокировки), и прерывание - это метод какого-либо другого потока для устранения этой ошибки.

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

Итак, вкратце, ваши варианты обработки должны следовать этому списку:


  1. По умолчанию add to throws .

  2. Если не разрешено добавлять в throws, создайте исключение RuntimeException (e). (Лучший выбор из нескольких ошибочных вариантов)

  3. Только когда вы знаете явную причину прерывания, обрабатывайте его по своему усмотрению. Если обработка локальна для вашего метода, сбросьте прерывание вызовом Thread.currentThread().interrupt() .

java multithreading exception