Подтвердить что ты не робот

Почему InterruptedExceptions очищают поток с прерыванием?

Если поток прерывается внутри Object.wait() или Thread.join(), он выдает InterruptedException, который сбрасывает состояние прерывания потока. I. e., Если у меня такой петли внутри a Runnable.run():

while (!this._workerThread.isInterrupted()) {
    // do something
    try {
        synchronized (this) {
            this.wait(this._waitPeriod);
        }
    } catch (InterruptedException e) {
        if (!this._isStopping()) {
            this._handleFault(e);
        }
    }
}

поток будет продолжать работать после вызова interrupt(). Это означает, что я должен явно вырваться из цикла, проверив мой собственный флаг остановки в состоянии цикла, перестроил исключение или добавил break.

Теперь это не совсем проблема, так как это поведение хорошо документировано и не мешает мне делать что-либо так, как я хочу. Тем не менее, я, похоже, не понимаю концепции, лежащей в основе этого: почему поток не считается прерванным после того, как исключение было выброшено? Подобное поведение также возникает, если вы получаете прерванный статус с interrupted() вместо isInterrupted(), тогда тоже поток будет только прерываться один раз.

Я делаю что-то необычное здесь? Например, чаще всего ловить InterruptedException вне цикла?

(Несмотря на то, что я не совсем новичок, я пометил этого "новичка", потому что для меня это очень простой вопрос, глядя на него.)

4b9b3361

Ответ 1

Идея заключается в том, что прерывание следует обрабатывать один раз. Если явный InterruptedException не очистил флаг "прерывание", тогда большинство ловушек для InterruptedException должны были бы явно очистить этот флаг. И наоборот, вы можете "неяснить" флаг самоперерывом (Thread.currentThread().interrupt()). Дизайнеры Java пошли на семантику, которая большую часть времени сохраняла бы нажатия клавиш (т.е. Вы чаще всего хотите очистить флаг, чем держать его установленным).

Ответ 2

Напишите свой код, как этот, и вам не понадобится флаг:

try {
    while (!this._workerThread.isInterrupted()) {
        // do something
        synchronized (this) {
            this.wait(this._waitPeriod);
        }
        // do something else
    }
} catch (InterruptedException e) {
    // ignore ...
}

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

Ответ 3

Это потому, что InterruptedException считается аномальным событием, в котором кто-то пытается остановить поток извне.

Если вы хотите прервать поток, вы просто нарушаете его условие цикла, устанавливая логическое или что-то подобное. Или вы используете .wait() и .notify() изнутри этого потока. Но если вы делаете wait() извне:

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

Ответ 4

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

Это нужно только один раз в любом месте, которое вы вызываете из своего кода, поэтому, чтобы иметь возможность прерывать поток, а затем использовать прерывистый бит для безопасного управления потоком изнутри указанного потока, вам необходимо на 100% убедитесь, что каждый фрагмент кода, который вы вызываете, не очищает прерываемый бит по ошибке. Это очень сложно сделать, когда библиотеки задействованы, но даже если вы могли бы учитывать каждую отдельную библиотеку, которую вы используете в своем коде, она все еще не учитывает багги JRE, который может сделать ту же ошибку.

Тот факт, что автор требует, чтобы один редактор библиотеки (или JRE!) не интересовался или не думал о прерываниях, чтобы нарушить логику кода, который требует его, показывает, что это неправильное действие по умолчанию. Кто-то, кто не заботится о прерывистом потоке, вероятно, не потрудится reset после ловли InterruptedException - может быть, они даже не знают, что он существует! Если для ловли InterruptedException не было reset состояние прерывания потока, то любой, кто не знал о прерванном бите, автоматически "поступил бы правильно" и не стал бы проблемой для любого вызывающего кода, основанного на прерываниях. Любой, кто потребовал очистить его, все равно может сделать это вручную, но тогда это будет явное действие, которое, скорее всего, будет правильным, чем обычно непреднамеренный побочный эффект отвлечения проверенного исключения InterruptedException. Поскольку он стоит прямо сейчас, если вы полагаетесь на прерывистый бит потока, любой из ваших стека вызовов, который вызывает Thread.sleep() небрежно, может потенциально испортить вам день.

В результате большинство многопотоковых кодов Java просто дублируют модель прерывания потока Java с полем экземпляра "isRunning" и некоторым механизмом, чтобы перевернуть его как обходной путь.