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


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

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

или

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

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

7 211

7 ответов:

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

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

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

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

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

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

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

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

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

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

правильный выбор по умолчанию-добавить 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