Por que invocar Thread.currentThread.interrupt () em um bloco InterruptException de captura?

155

Por que invocar o método Thread.currentThread.interrupt()no bloco catch?

Hesey
fonte

Respostas:

160

Isso é feito para manter o estado .

Ao capturar InterruptExceptione engolir, você basicamente impede que métodos / grupos de segmentos de nível superior notem a interrupção. O que pode causar problemas.

Ao ligar Thread.currentThread().interrupt(), você define o sinalizador de interrupção do encadeamento, para que os manipuladores de interrupção de nível mais alto notem e possam lidar com isso adequadamente.

A Concorrência Java na Prática discute isso com mais detalhes no Capítulo 7.1.3: Respondendo à interrupção . Sua regra é:

Somente o código que implementa a diretiva de interrupção de um encadeamento pode engolir uma solicitação de interrupção. O código de tarefa e biblioteca de uso geral nunca deve engolir solicitações de interrupção.

Péter Török
fonte
15
Na documentação , declara-se que "por convenção, qualquer método que sai lançando um InterruptedException status de interrupção limpa quando o faz. Acho que isso torna a resposta mais clara em termos de por que você precisa preservar o status de interrupção.
Stelios Adamantidis
Também é importante notar que a interrupt()chamada é a única maneira de definir o sinalizador interrompido depois que você recebe uma notificação sobre esse estado por meio do outro "mecanismo de entrega" - o InterruptedExceptione deseja ou não pode jogá-lo novamente.
Sevo
67

Eu acho que esse exemplo de código deixa as coisas um pouco claras. A classe que faz o trabalho:

   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();
            }
        }
    }

A classe principal:

   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();
        }

    }

Tente interromper a chamada sem restaurar o status.

Ajay George
fonte
13
então a conclusão é ??
Scott混合理论
3
Obrigado. Entendo o que você quer dizer agora: repl.it/@djangofan/InterruptedThreadExample
djangofan
20

Nota:

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

Como interrompo um encadeamento que aguarda longos períodos (por exemplo, entrada)?

Para que essa técnica funcione, é essencial que qualquer método que capte uma exceção de interrupção e não esteja preparado para lidar com ela reafirme a exceção imediatamente. Dizemos reafirmações em vez de repetições, porque nem sempre é possível repetir a exceção. Se o método que captura a InterruptedException não for declarado para lançar essa exceção (marcada), ele deverá "se reinterromper" com o seguinte encantamento:

Thread.currentThread().interrupt();

Isso garante que o Thread reraise a InterruptedException assim que possível.

Bert F
fonte
2

Eu consideraria uma prática ruim ou pelo menos um pouco arriscada. Geralmente, os métodos de nível superior não realizam operações de bloqueio e nunca serão vistos InterruptedExceptionlá. Se você o mascarar em todos os lugares em que executar operações interruptíveis, nunca o conseguirá.

A única lógica para Thread.currentThread.interrupt()e não gerar nenhuma outra exceção ou solicitação de interrupção de sinalização de qualquer outra maneira (por exemplo, definir interruptedvariável variável local no loop principal de um encadeamento) é a situação em que você realmente não pode fazer nada com a exceção, como nos finallyblocos.

Veja a resposta de Péter Török, se você quiser entender melhor as implicações da Thread.currentThread.interrupt()chamada.

Piotr Findeisen
fonte
0

Consulte java doc

Se esse encadeamento estiver bloqueado em uma chamada de wait (), join (), sleep (long), seu status de interrupção será limpo e ele receberá uma InterruptedException.

Se esse encadeamento estiver bloqueado em uma operação de E / S, o status de interrupção do encadeamento será definido e o encadeamento receberá uma ClosedByInterruptException.

Se esse encadeamento estiver bloqueado em um seletor, o status de interrupção do encadeamento será definido e ele retornará imediatamente da operação de seleção.

Se nenhuma das condições anteriores for mantida, o status de interrupção deste encadeamento será definido.

Portanto, se você alterar o método sleepBabySleep () no @Ajay George Answer à operação de E / S ou apenas um sysout, não será necessário definir o status novamente para interromper o programa. (BTW, eles nem lançam InterruptedException)

Assim como @ Péter Török disse => Isso é feito para manter o estado. (Especialmente para o método que lançará InterruptedException)

Michael Ouyang
fonte