Обработка InterruptedException в Java

в чем разница между следующими способами обработки InterruptedException? Как лучше всего это сделать?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

или

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

EDIT: я также хотел бы знать, в каких сценариях используются эти два.

7 ответов


Вы, наверное, пришли, чтобы задать этот вопрос, потому что вы назвали метод, который бросает InterruptedException.

прежде всего, вы должны увидеть throws InterruptedException для чего это: часть подписи метода и возможный результат вызова метода, который вы вызываете. Поэтому начните с принятия того факта, что an 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 тот факт, что поток был прерван возможно, это все еще важно. В частности, код, вызывающий ваш метод, может быть заинтересован в том, произошло ли прерывание во время выполнения вашего метода. Поэтому вы должны зарегистрировать факт прерывания, установив флаг прерывания: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;
}

этот пост был переписан как статья здесь.


как это случилось, я как раз читал об этом сегодня утром по дороге на работу в Параллелизм Java На Практике Брайан Гетц. В основном он говорит, что вы должны сделать одну из двух вещей

  1. передать InterruptedException - объявите свой метод, чтобы бросить checked InterruptedException Так что ваш абонент должен иметь дело с ним.

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


что вы пытаетесь сделать?

на InterruptedException бросается, когда поток ждет или спит, а другой поток прерывает его с помощью interrupt метод в классе Thread. Поэтому, если вы поймаете это исключение, это означает, что поток был прерван. Обычно нет смысла звонить Thread.currentThread().interrupt(); опять же, если вы не хотите проверить" прерванный " статус потока откуда-то еще.

относительно вашего другого варианта бросания RuntimeException, он, кажется, не очень мудрый поступок (кто поймает это? как с этим справиться?), но трудно сказать без дополнительной информации.


для меня ключевая вещь об этом: InterruptedException-это не что-то неправильное, это поток, делающий то, что вы сказали ему сделать. Поэтому переосмысление его, обернутого в RuntimeException, имеет нулевой смысл.

во многих случаях имеет смысл переосмыслить исключение, завернутое в исключение RuntimeException, когда вы говорите, что я не знаю, что пошло не так, и я не могу ничего сделать, чтобы исправить это, я просто хочу, чтобы он вышел из текущего потока обработки и попал в любое приложение обработчик исключений у меня есть, чтобы он мог его зарегистрировать. Это не относится к InterruptedException, это просто поток, отвечающий на вызов interrupt (), он бросает InterruptedException, чтобы помочь своевременно отменить обработку потока.

таким образом, распространите InterruptedException или съешьте его разумно (то есть в месте, где он выполнит то, что должен был сделать) и сбросьте флаг прерывания. Обратите внимание, что флаг прерывания очищается когда InterruptedException выбрасывается; предположение разработчиков библиотеки Jdk заключается в том, что улавливание исключения равнозначно его обработке, поэтому по умолчанию флаг очищается.

Так что определенно первый способ лучше, второй опубликованный пример в вопросе не полезен, если вы не ожидаете, что поток действительно будет прерван, и прерывание его равносильно ошибке.

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


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

Java не будет случайным образом бросать InterruptedException, все советы не повлияют на ваш приложение, но я столкнулся с случаем, когда разработчик, следуя стратегии "ласточки", стал очень неудобным. Команда разработала большой набор тестов и использовать нити.Много спать. Теперь мы начали запускать тесты на нашем сервере CI, и иногда из-за дефектов кода застревали в постоянных ожиданиях. Чтобы сделать ситуацию хуже, при попытке отменить задание CI он никогда не закрывался, потому что поток.Прерывание, которое было предназначено для прерывания теста, не прерывало работу. У нас чтобы войти в поле и вручную убить процессы.

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

существует очень рациональный аргумент, который должен быть сделан, что InterruptedException должно быть самим RuntimeException, так как это будет способствовать лучшей обработке "по умолчанию". Это не Исключение RuntimeException только потому, что разработчики придерживались категориального правила, что исключение RuntimeException должно представлять ошибку в коде. Поскольку InterruptedException не возникает непосредственно из-за ошибки в вашем коде, это не так. Но реальность такова, что часто возникает исключение InterruptedException, потому что в вашем коде есть ошибка (т. е. бесконечный цикл, мертвая блокировка), и прерывание-это метод другого потока для борьбы с этой ошибкой.

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

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

  1. по умолчанию добавьте к броскам.
  2. если не разрешено добавлять к броскам, бросьте RuntimeException (e). (лучший выбор из нескольких плохих вариантов)
  3. только когда вы знаете явную причину прерывания, обрабатывайте по желанию. Если ваша обработка локальный для вашего метода, а затем сброс прерывается вызовом потока.currentThread().нарушать.)(

Я просто хотел добавить последний вариант к тому, что большинство людей и статей упомянуть. Как заявил mR_fr0g, важно правильно обрабатывать прерывание либо:

  • распространение InterruptException

  • восстановить состояние прерывания в потоке

дополнительно:

  • пользовательская обработка прерывания

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

Я настоятельно рекомендую понять тему, но эта статья является хорошим источником информации:http://www.ibm.com/developerworks/java/library/j-jtp05236/


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

один случай будет в случае, если у вас есть задача (блокировка) очереди. Если у вас есть поток демона, обрабатывающий эти задачи, и вы не прерываете поток самостоятельно (насколько мне известно, jvm не прерывает daemon threads on JVM shutdown), я не вижу способа для прерывания, и поэтому его можно просто игнорировать. (Я знаю, что поток демона может быть убит jvm в любое время и поэтому в некоторых случаях непригоден).

изменить: Другой случай может быть защищенными блоками, по крайней мере, на основе учебника Oracle в: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html