Зачем использовать Thread.currentThread(). IsInterrupted() вместо Thread.interrupted() при реализации Runnable?

В stackoverflow я часто вижу использование Thread.currentThread().isInterrupted(). При реализации Runnable и использовании его в цикле while, например:

public void run() {
  while(!Thread.currentThread().isInterrupted()) { ... }
}

есть ли разница в использовании Thread.interrupted() (кроме того, что флаг interrupted очищается при использовании interrupted())?

Я также видел Thread.currentThread().interrupted(). Это правильный способ его использования или Thread.interrupted() достаточно?

Ответ 1

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

Сначала нужно определить, считается ли код проверки статуса прерывания (или обработки InterruptedException) "владельцем" потока. Если да, то в некоторых ограниченных случаях может быть целесообразным усвоить (или просто не выбросить) InterruptedException, а также прерванный статус, поскольку владелец реализует политику отмены потока (Goetz, Java Concurrency в Practice, p 143).

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

  • Оставьте статус прерывания потока очищенным, но введите InterruptedException. (Это то, что делает Thread.sleep()).
  • Сохранять статус прерывания.

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

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

if (Thread.currentThread().isInterrupted()) doSomething;

чем

if (Thread.interrupted()) {
    Thread.currentThread().interrupt();
    doSomething;
}

Кроме того, как и в вашем исходном вопросе, если вы использовали Thread.interrupted() как условие в цикле while, после разрыва цикла вы не знали бы, завершится ли это, потому что Thread.interrupted() возвращен true или какой-либо другой условие изменено или выполняется оператор break. Поэтому в этом случае использование Thread.currentThread().isInterrupted() действительно является вашим единственным вариантом. (Конечно, вы могли бы также закодировать цикл таким образом, что единственная причина, по которой он должен был выйти, состоит в том, что поток прерывается, но тогда ваш код будет хрупким, потому что после цикла вы должны повторно прервать поток, и если позже появится кто-то другой и изменил код, чтобы также вырваться из цикла по какой-то другой причине, тогда вы прервали бы поток, когда он не был первоначально прерван.)

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

предупреждение: статический метод статического класса должен быть присвоен типом имени Thread, а не выражением

Некоторые другие инструменты могут работать аналогично, например Eclipse, которые показывают:

Статический метод прерванный() из типа Thread должен быть доступен статическим способом

Ответ 2

Просто отвечая на последнюю часть вашего вопроса...

Я также видел Thread.currentThread().interrupted(). Это правильный способ его использования или Thread.interrupted() достаточно?

В чисто функциональных терминах они означают одно и то же.

Но с точки зрения удобочитаемости,

    Thread.currentThread().interrupted()

делает вид, что вы вызываете метод экземпляра... но вы этого не делаете. Следовательно,

    Thread.interrupted()

лучше. И, конечно, НЕ делайте этого:

    Thread someThread = ...
    someThread.interrupted()

Похоже, вы бы тестировали someThread, но на самом деле вы тестируете текущий поток. Очень вводящий в заблуждение!

Ответ 3

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

Главное, чтобы знать, что вы делаете с прерванным флагом: оставить его в состоянии, которое вы специально не намеревались, определенно не будет делать только тонкую разницу.

Никогда не используйте Thread.currentThread().interrupted(). Это просто вводит в заблуждение, особенно если у вас есть someOtherThread.interrupted().

Ответ 4

есть ли разница в использовании Thread.interrupted() (кроме того, что прерываемый флаг очищается при использовании interrupted())?

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

Что касается разницы между Thread.currentThread().interrupted() vs. Thread.interrupted(), то нет функциональной разницы, но последняя короче, поэтому используйте ее.