3

Я читаю Goetz's Java Concurrency In Practice, где показан этот пример код:Является ли этот пример кода просто поспешным или будет ли причина здесь использовать прерывание()

public final class Indexer implements Runnable { 

    private final BlockingQueue<File> queue; 

    public Indexer(BlockingQueue<File> queue) { 
     this.queue = queue; 
    } 

    @Override 
    public void run() { 
     try { 
      while (true) { 
       queue.take(); 
      } 
     } catch (InterruptedException e) { 
      Thread.currentThread().interrupt(); 
     } 
    } 

} 

с описанием:

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

В примере кода «код выше стека вызовов» никогда не будет видеть прерывание, если этот код выполнен - ​​или я делаю неправильный вывод? Поток здесь просто умирает после звонка interrupt(), правильно?

Таким образом, единственный способ, которым этот interrupt() может быть полезен, если он находится внутри цикла, правильно?

ответ

2

Нить здесь просто умирает после вызова прерывания(), правильно?

Выполняющая нить не закончится после прерывания, вы думаете, Thread#stop. Thread thread может продолжать работать даже после завершения выполнения. Runnable - это просто задача, выполняемая потоком.

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

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

+0

Я не на 100% уверен, что вы пытаетесь сказать. OP явно предполагает, что любой экземпляр данного класса «Indexer» будет делегатом «Thread», и в этом случае не будет «кода выше стека вызовов». Кажется, вы говорите: «Не думайте об этом! Вы не знаете, кто называет этот метод' run() '. Я прав? –

+0

Это правильно. Это может быть так, что отношение индексатора к потоку равно 1-1.То, что я делаю (подобно Goetz), состоит в том, что это может быть не так, и если это может быть не так, всегда следует распространять прерывание. –

+0

Немного абразивно сказать, что я думал 'Thread # stop()'. У «Runnable» просто заканчивается код, который запускается после блока «try-catch». Не могли бы вы привести пример кода, чтобы продемонстрировать, что вы подразумеваете под «Thread», отвечая на какой-либо другой запрос на отмену? Или, по крайней мере, расширить идею? – Adam

0

The Java documentation определяет эффект Thread.interrupt() как

Если поток блокируется в вызове ожидания(), ждать (долго), или ждать (долго, INT) методы класса Object, или join(), join (long), join (long, int), sleep (long) или sleep (long, int), методы этого класса, тогда его статус прерывания будет очищен и он получит InterruptedException.

Если этот поток заблокирован в операции ввода-вывода на InterruptibleChannel, тогда канал будет закрыт, статус прерывания потока будет установлен, и поток получит исключение ClosedByInterruptException.

Если этот поток заблокирован в Селекторе, тогда будет установлен статус прерывания потока, и он немедленно вернется из операции выбора, возможно, с ненулевым значением, как если бы был вызван метод пробуждения селектора.

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

Обратите внимание на последний абзац; по существу, если ваш поток в настоящее время не выполняет одну из указанных операций стиля ожидания, прерывание потока просто устанавливает флаг, но не влияет на поток управления иным образом. Это верно независимо от того, происходит ли вызов из другого потока или самого потока жертвы.

Другими словами:

try { 
    while (true) { 
     queue.take(); 
    } 
} catch (InterruptedException e) { 
    Thread.currentThread().interrupt(); 
} 

// When an interrupt is received, the thread resumes 
// execution here. It will notice the effect of the 
// Thread.currentThread().interrupt() only, if it tries 
// to perform any of the mentioned special wait operations 
// here. 

synchronized (someLockObject) { 

    while (!someCondition) { 

     // Will immediately terminate with an `InterruptedException`, 
     // since we properly restored the interrupt status 
     // above. 

     someLockObject.wait(); 
    } 
} 

Этот метод обеспечивает, что весь код в текущем вызова стека имеет возможность распознавать прерывания, и выполнять необходимую очистку и «упорядоченный» закрыли , Если вы не восстановите состояние прерывания, тогда код, следующий за /catch, может ошибочно предположить, что все в порядке, и что он должен нормально возобновляться.

+0

, так что вы в основном говорите, что г-н Гёц мог бы немного объяснить свой пример, чтобы мой вопрос никогда не возник? – Adam

+0

Это один из способов выразить это. Другой может быть: «Не останавливайтесь в книге. Также прочтите документацию и узнайте, что на самом деле делает Thread.interrupt() ...» – Dirk

+0

, пожалуйста, не просто скажите RTFM. К сожалению, javadoc не так всесторонне. Поскольку все, кто прочитал текущий заголовок заголовка SO на документации, будут распознавать, здесь примеры являются ключевыми, и эта проблема не рассматривается непосредственно, не говоря уже о проиллюстрированном на примере с помощью «#interrupt()» или уровня javadoc на уровне класса. Ваш пример добавляет дополнительный код после вызова 'interrupt' - есть ли другие ситуации, когда вызов' interrupt' может быть последним кодом, выполняемым в 'Runnable', но' Thread' или связанным с ним 'Future' или что-то еще else читает этот статус прерывания? – Adam

1

В Java вам необходимо обработать прерывание потока. Для этого необходим опрос Thread.isInterrupted(). Когда ваш код является частью Runnable, вам нужно установить прерванный флаг Thread в случае InterruptedException, чтобы последующие задачи, выполняемые этим потоком (код выше стека вызовов), могли опросить Thread.isInterrupted() и соответствующим образом обработать прерывание. Это также дает потоку возможность выйти чисто, а не убивать его сразу. Это то, что делает Thread.stop().

Object.wait и Thread.sleep - это два примера, которые сначала проверяют флаг и бросают InterruptedException, если он установлен.

1

Я столкнулся с этим при написании примеров игрушек, я ненавижу вставить что-то вроде Thread.currentThread().interrupt(), когда в примере он ничего не делает, но я также хочу, чтобы мой пример демонстрировал хорошую практику, что означает настройку флаг прерывания.

Если вы передадите Runnable в Thread, то, очевидно, нет ничего, что прочитает прерывание, восстановленное как последнее, что делает Runnable. Когда поток заканчивается, флаг прерывания больше не доступен.

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

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

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

Смежные вопросы