Зачем вызывать Thread.currentThread.interrupt () в блоке перехвата InterruptException?

Ответы:

160

Это сделано, чтобы сохранить состояние .

Когда вы ловите InterruptException и проглатываете его, вы по существу не допускаете, чтобы любые методы / группы потоков более высокого уровня замечали прерывание. Что может вызвать проблемы.

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

Java Concurrency на практике обсуждает это более подробно в главе 7.1.3: Реагирование на прерывание . Его правило таково:

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

Петер Тёрёк
источник
15
В документации говорится, что «по соглашению любой метод, который завершается, выбрасывая InterruptedException очищенное состояние прерывания, когда это делает. Я думаю, что это делает ответ более понятным с точки зрения того, почему вам нужно сохранять состояние прерывания.
Stelios Adamantidis
Стоит также отметить, что interrupt()вызов - это единственный способ установить флаг прерывания, как только вы получили уведомление об этом состоянии через другой «механизм доставки» - InterruptedExceptionи он желает или не может повторно его выбросить.
Sevo
67

Я думаю, что этот пример кода проясняет ситуацию. Класс, который делает работу:

   public class InterruptedSleepingThread extends Thread {

        @Override
        public void run() {
            doAPseudoHeavyWeightJob();
        }

        private void doAPseudoHeavyWeightJob() {
            for (int i=0;i<Integer.MAX_VALUE;i++) {
                //You are kidding me
                System.out.println(i + " " + i*2);
                //Let me sleep <evil grin>
                if(Thread.currentThread().isInterrupted()) {
                    System.out.println("Thread interrupted\n Exiting...");
                    break;
                }else {
                    sleepBabySleep();
                }
            }
        }

        /**
         *
         */
        protected void sleepBabySleep() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //e.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }
    }

Основной класс:

   public class InterruptedSleepingThreadMain {

        /**
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            InterruptedSleepingThread thread = new InterruptedSleepingThread();
            thread.start();
            //Giving 10 seconds to finish the job.
            Thread.sleep(10000);
            //Let me interrupt
            thread.interrupt();
        }

    }

Попробуйте вызвать прерывание без возврата статуса.

Аджай Джордж
источник
13
так что вывод ??
Скотт 理论 理论
3
Спасибо. Теперь я понимаю, что вы имеете в виду: repl.it/@djangofan/InterruptedThreadExample
djangofan
20

Примечание:

http://download.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

Как мне остановить поток, который ждет в течение длительного времени (например, для ввода)?

Чтобы этот метод работал, очень важно, чтобы любой метод, который перехватывает исключение прерывания и не готов обработать его, немедленно переустанавливает исключение. Мы говорим, скорее, подтверждает, а не отбрасывает, потому что не всегда возможно отбросить исключение. Если метод, который перехватывает InterruptedException, не объявлен для генерирования этого (проверенного) исключения, то он должен «сам себя прервать» следующим заклинанием:

Thread.currentThread().interrupt();

Это гарантирует, что Поток повторно вызовет InterruptedException, как только сможет.

Берт F
источник
2

Я бы посчитал это плохой практикой или хотя бы немного рискованной. Обычно методы более высокого уровня не выполняют блокирующих операций и никогда их InterruptedExceptionтам не увидят . Если вы маскируете его в каждом месте, где выполняете прерываемую операцию, вы никогда не получите его.

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

См. Ответ Петера Тёрока, если вы хотите лучше понять значение этого Thread.currentThread.interrupt()звонка.

Петр Финдейзен
источник
0

См. Документ Java

Если этот поток заблокирован при вызове wait (), join (), sleep (long), то его состояние прерывания будет очищено, и он получит исключение InterruptedException.

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

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

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

Итак, если вы измените метод sleepBabySleep () в @Ajay George Ответ на операцию ввода-вывода или просто на sysout, вам не нужно возвращать статус обратно, чтобы остановить программу. (Кстати, они даже не генерируют InterruptedException)

Точно так же, как сказал @ Петер Тёрёк => Это сделано для сохранения состояния. (И особенно для метода, который будет генерировать InterruptedException)

Майкл Оуян
источник