Почему проглатывание InterruptedException подходит для подклассов Thread?

В статье Брайана Гетца о том, как обрабатывать InterruptedException, стоит один абзац. вне:

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

Я не понимаю. Причина в том, что Runnable может обрабатываться пулом потоков, тогда как Thread - это то, что вы запускаете самостоятельно?


person ashitaka    schedule 20.05.2009    source источник


Ответы (3)


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

Я ненавижу InterruptedException, я думаю, что это дает проверенным исключениям дурную славу, и эта статья не меняет эту точку зрения. Если бы было так важно, чтобы это исключение проходило вверх по стеку вызовов, Runnable.run() должен был бы объявить его в объявлении метода, чтобы вы могли просто повторно сгенерировать его, или это должно было быть непроверенное исключение по той же причине, что SecurityException является непроверенным исключение.

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

person Yishai    schedule 20.05.2009
comment
Реализация Callable может помочь вам вернуть логическое значение. Callable также позволяет создавать исключения вверх. - person James McMahon; 20.05.2009
comment
Возвращать логическое значение было бы плохой идеей. Если он не очищает состояние, пропустите возвращаемое значение в типичном цикле ожидания, и он вращается. Если он очищает состояние, то мы прервали поток. Отмеченное исключение делает это ясным (ну, возможно, учитывая код, который я видел, не все). Непроверенное исключение, вероятно, приведет к выходу разумным образом. - person Tom Hawtin - tackline; 20.05.2009
comment
@Yishai, я не так сильно ненавижу InterruptedException. С точки зрения многопоточности приятно знать, что сигнатура метода является прерываемой. - person ashitaka; 20.05.2009
comment
@ Том Хотин, учитывая опасения в статье, я согласен с тем, что не обязательно делать это просто логическим значением. Это было определенно информативно для меня. При этом здесь есть много компромиссов, практический опыт показывает, что проверенный маршрут исключения не сработал, в основном потому, что повторный вызов не является подходящим ответом, что удивительно и, следовательно, неправильно обрабатывается при глотании. - person Yishai; 20.05.2009

Я бы сказал, что расширение Thread было ненужным, и поэтому реализация Runnable предпочтительнее.

Но важно то, что код знает, что поток собирается выйти. Если ваш код является частью какого-то универсального интерфейса обратного вызова, вы не можете знать, как вас используют. Вы можете быть переданы в пул потоков (действительно, нам, вероятно, следует использовать пулы, а не создавать Thread в неподходящих местах кода). OTOH, обычно Runnable является анонимным внутренним классом и, следовательно, на исходном уровне является частью включающего метода, который знает, что происходит.

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

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

Библиотеки обычно неправильно обрабатывают прерывания. ИМО, прерывания не имеют смысла. Без них жизнь была бы намного проще, к сожалению, они дают о себе знать.

person Tom Hawtin - tackline    schedule 20.05.2009

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

Я думаю, что InterruptedException является проверенным исключением. InterruptedException — это способ запросить отмену задач. Предположим, кто-то написал задачу в форме Runnable, включающую блокирующий метод, выбрасывающий InterruptedException. Если бы это не было проверенным исключением, то, если вы не будете осторожны, вы можете не подумать о том, чтобы воздействовать на InterruptedException (таким образом, отменить) и выполнить собственную очистку.

public class MyTask implements Runnable {
    public void run() {
        while (someCondition) {
            Object value = someBlockingQueue.take();
            // act on the value and loop back
        }
    }
}

Поскольку InterruptedException является проверенным исключением, то, как моя задача должна реагировать на прерывание (отмену), находится в центре внимания.

public class MyTask implements Runnable {
    public void run() {
        while (someCondition) {
            try {
                Object value = someBlockingQueue.take();
                // act on the value and loop back
            } catch (InterruptedException e) {
                // I'm being cancelled; abort
                cleanUp();
                // restore the interrupt
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}
person sjlee    schedule 17.07.2009
comment
Я думаю, что, скорее всего, вы захотите сделать это cleanUp() независимо от того, было ли исключение или нет - оно должно жить в блоке finally{}. - person Trejkaz; 30.11.2010