Методы, снижающие флаг Thread.interrupt()

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

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

В настоящее время я знаю, что следующее очистит флаг прерывания:

Thread.interrupted()
Thread.sleep(long)
Thread.join()
Thread.join(long)
Object.wait()
Object.wait(long)

Что еще мне не хватает? Спасибо вам

Ответы

Ответ 1

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

Важно уточнить, что следующие методы очищают флаг прерывания, просто называя их:

Thread.interrupted()
Thread.isInterrupted(true) -- added to your list

По этой причине Thread.currentThread().isInterrupted() всегда следует использовать вместо этого.

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

Thread.sleep(long)
Thread.join()
Thread.join(long)
Thread.join(int, long) – added to your list
Object.wait()
Object.wait(long)
Object.wait(int, long) – added to your list
BlockingQueue.put(...) – added to your list
BlockingQueue.offer(...) – added to your list
BlockingQueue.take(...) – added to your list
BlockingQueue.poll(...) – added to your list
Future.get(...) – added to your list

Обратите внимание на, что правильный шаблон с любым кодом, который ловит InterruptedException, - это немедленно перерезать поток. Мы делаем это, если другие полагаются на метод thread.isInterrupted():

try {
    ...
} catch (InterruptedException e) {
    // immediately re-interrupt the thread
    Thread.currentThread().interrupt();
    // log the exception or [likely] quit the thread
}

Код JUnit, который демонстрирует некоторые из следующих:

assertFalse(Thread.currentThread().isInterrupted());
// you can do this from another thread by saying: someThread.interrupt();
Thread.currentThread().interrupt();
// this method does _not_ clear the interrupt flag
assertTrue(Thread.currentThread().isInterrupted());
// but this one _does_ and should probably not be used
assertTrue(Thread.interrupted());
assertFalse(Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
assertTrue(Thread.currentThread().isInterrupted());
try {
    // this throws immediately because the thread is _already_ interrupted
    Thread.sleep(1);
    fail("will never get here");
} catch (InterruptedException e) {
    // and when the InterruptedException is throw, it clears the interrupt
    assertFalse(Thread.currentThread().isInterrupted());
    // we should re-interrupt the thread so other code can use interrupt status
    Thread.currentThread().interrupt();
}
assertTrue(Thread.currentThread().isInterrupted());

Ответ 2

Общее соглашение следующее: любой метод, который выбрасывает InterruptedException (+ Thread.interrupted()), удаляет флаг прерывания.

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

Ответ 3

Здесь пример SUPER FUN:

ch.qos.logback.core.AsyncAppenderBase до версии 1.1.4 ловит и проглатывает InterruptedException без сброса флага в потоке.

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