Всегда вызывайте Thread.currentThread(). Interrupt(); при перехвате InterruptedException?

В этой статье developerWorks developerWorks developerWorks:

"Когда-нибудь допустимо усвоить прерывание, когда вы знаете, что поток вот-вот выйдет. Этот сценарий возникает только тогда, когда класс, вызывающий прерывистый метод, является частью Thread, а не Runnable [...]".

Я всегда реализовал Runnable для своих потоков. Предоставление реализации Runnable следующим образом:

public class View() implements Runnable {
    @Overload
    public void run(){
        Thread worker = new Thread(new Worker());
        worker.start();
        do{
            try{
                TimeUnit.SECONDS.sleep(3);
                updateView();
            }catch(InterruptedException e){
                worker.interrupt();
                // Thread.currentThread().interrupt();
                return;
            }
        }while(true);
    }

    protected void updateView(){
        // …
    }
}

Действительно ли нужно позвонить Thread.currentThread().interrupt(); прямо перед моим оператором return;? Неужели return; уже выполняет очищенный выход enaugh? Какая польза назвать это? В статье говорится, что это должно быть сделано, потому что иначе "[...] код выше в стеке вызовов не сможет узнать об этом [...]". Какая польза от потока в Thread.State.TERMINATED с прерванным флагом, установленным над ним без него при завершении работы приложения? Можете ли вы привести мне пример, где код вне Runnable проверяет прерванный флаг по разумной причине?

BTW, это лучший дизайн кода для расширения Thread вместо реализации Runnable?

Ответ 1

Сбрасывает флаг прерывания. Этот бюллетень JavaSpecialists более подробно описывает эту запутанную тему.

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

Итак, если вы знаете, что ваш код не будет использоваться другим компонентом, вам не нужно повторно прерывать. Однако я действительно не сделал бы эту небольшую оптимизацию. Кто знает, как ваш код будет использоваться/повторно использоваться в будущем (даже путем копирования/вставки), и, следовательно, я бы reset флаг для каждого прерывания.

Ответ 2

Вот пример, где возврата недостаточно:

public void doSomething1() {
  while (someCondition1()) {
    synchronized {
       try {
         this.wait();
       } catch (InterruptedException e) {
         return; // Should be Thread.currentThread().interrupt();
       }
    }
  }
}
public void doSomething2() {
  while (someCondition2()) {
    doSomething1();
  }
}

Как исключение throw очищает прерванное состояние в следующий раз, когда doSomething1() выполняется, состояние очищается, и поток не завершается.

Ответ 3

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

Как заявил Брайан, он сбрасывает флаг прерывания, но это мало говорит. В вашем случае это ничего не сделает, и View -Thread продолжит работу.

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

Используя встроенную функциональность, есть два варианта:

  • Имейте основной цикл внутри блока try для InterruptedException. Таким образом, когда он будет прерван, вы будете выброшены из цикла, и метод завершится.
  • Вышеуказанное может быть плохим, если вам нужно сохранить состояние, так как оно может привести к повреждению состояния. В качестве альтернативы вы можете установить прерванный флаг (как сказано, когда он был брошен. Re-interrupt it Interrupt the Thread

В любом случае вам нужно проверить, что поток прерывается в вашем while-loop (с !Thread.currentThread().isInterrupted() -statement в while-loop), или он может/не выйти. Вы не выполняете один из первых вариантов и не проверяете флаг, поэтому ваш View -thread будет продолжать работать после прерывания.

Ответ 4

Нужно ли действительно называть Thread.currentThread().interrupt(); непосредственно перед оператором return;?

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

Не выполняет return; уже достаточно чистый выход?

Если вы уверены, что это последнее возвращение до того, как метод run() завершен, и поток выходит, то да, это не технически необходимо. Но см. Выше. Для потомков return; ничего не делает с флагом прерывания.

Вопрос в том, был ли ваш класс View обернут. Вы уверены, что когда вы вернетесь, вы выходите из Thread. Может быть, кто-то делегирует это. АОП может быть на месте, чтобы делать какие-то приборы.

Какая польза от вызова? В статье говорится, что это должно быть сделано, потому что иначе "[...] код выше в стеке вызовов не сможет узнать об этом [...]".

В общем, важно не проглатывать прерывание, когда ваш код вызывается каким-то кодом обертывания (делегирование, АОП и т.д.), которому нужен флаг прерывания. Если вы глотаете его, обертка не сможет его использовать. Но в этом случае нет никакой пользы.

Какая польза от потока в Thread.State.TERMINATED с прерванным флагом, установленным над ним без него при завершении работы приложения?

Ничего. Как только поток выходит из состояния прерывания, это бесполезно. И на самом деле, похоже, что состояние прерывания даже не сохраняется после того, как поток мертв.

Thread thread = new Thread(new Runnable() {
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("caught");
        }
    }
});
thread.start();
thread.interrupt();
System.out.println(thread.isInterrupted());
thread.join();
System.out.println(thread.isInterrupted());

Печать

true
caught
false

Можете ли вы привести мне пример, где код вне Runnable проверяет прерванный флаг по разумной причине?

Я не могу. Нет кода вне метода потока run(), если кто-то не обменивает ваш runnable в другом коде без вашего ведома.

Это может произойти, если вы используете ExecutorService, но в этом случае статус прерывания потока очищается с помощью wt.isInterrupted() до выполнения задания.

Итак, опять же, причина состоит в том, что это хороший шаблон и что важно в разработке программного обеспечения.