Qual é a diferença entre as seguintes formas de manuseio InterruptedException
? Qual é a melhor maneira de fazer isso?
try{
//...
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
OU
try{
//...
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
EDIT: Gostaria de saber também em quais cenários esses dois são usados.
Respostas:
Você provavelmente veio fazer essa pergunta porque chamou um método que lança
InterruptedException
.Primeiro de tudo, você deve ver
throws InterruptedException
o que é: Uma parte da assinatura do método e um possível resultado de chamar o método que você está chamando. Portanto, comece abraçando o fato de que umInterruptedException
é um resultado perfeitamente válido da chamada do método.Agora, se o método que você está chamando gera essa exceção, o que seu método deve fazer? Você pode descobrir a resposta pensando no seguinte:
Faz sentido para o método que você está implementando lançar um
InterruptedException
? Em outras palavras, é umInterruptedException
resultado sensato ao chamar seu método?Se sim , então
throws InterruptedException
deve ser parte de sua assinatura do método, e você deve deixar a propagar exceção (ou seja, não pegá-lo em tudo).Se não , você não deve declarar seu método com
throws InterruptedException
e deve (deve!) Capturar a exceção. Agora, duas coisas são importantes a serem lembradas nessa situação:Alguém interrompeu seu tópico. É provável que alguém esteja ansioso para cancelar a operação, encerrar o programa normalmente ou qualquer outra coisa. Você deve ser educado com alguém e retornar do seu método sem mais delongas.
Mesmo que seu método consiga produzir um valor de retorno sensato no caso de
InterruptedException
o fato de o encadeamento ter sido interrompido, ainda pode ser importante. Em particular, o código que chama seu método pode estar interessado em saber se ocorreu uma interrupção durante a execução do seu método. Portanto, você deve registrar o fato de uma interrupção configurando o sinalizador interrompido:Thread.currentThread().interrupt()
Agora, deve ficar claro que apenas fazer
throw new RuntimeException(e)
é uma má ideia. Não é muito educado para quem liga. Você pode inventar uma nova exceção de tempo de execução, mas a causa raiz (alguém deseja que o encadeamento interrompa a execução) pode se perder.Outros exemplos:
Esta postagem foi reescrita como um artigo aqui .
fonte
interrupt()
para preservar o status interrompido. Qual é a razão por trás de não fazer isso em umThread.sleep()
?Thread.currentThread.interrupt()
umInterruptedException
bloco catch apenas definirá o sinalizador de interrupção. Não fará com que outroInterruptedException
seja jogado / preso em umInterruptedException
bloco de captura externo .Por acaso, eu estava lendo sobre isso hoje de manhã no meu caminho para trabalhar em Java Concurrency In Practice por Brian Goetz. Basicamente, ele diz que você deve fazer uma de três coisas
Propague o
InterruptedException
- Declare seu método para jogar o marcado,InterruptedException
para que o chamador tenha que lidar com ele.Restaurar a interrupção - às vezes você não pode jogar
InterruptedException
. Nesses casos, você deve capturarInterruptedException
e restaurar o status da interrupção chamando ointerrupt()
método no,currentThread
para que o código mais alto na pilha de chamadas possa ver que uma interrupção foi emitida e retornar rapidamente do método. Nota: isso só é aplicável quando o seu método possui semântica "try" ou "best esforço", ou seja, nada crítico aconteceria se o método não atingir seu objetivo. Por exemplo,log()
ousendMetric()
pode ser esse método, ouboolean tryTransferMoney()
, mas nãovoid transferMoney()
. Veja aqui para mais detalhes.Uninterruptibles
.Uninterruptibles
assuma o código padrão, como no exemplo Tarefa não cancelável no JCIP § 7.1.3.fonte
O que você está tentando fazer?
A
InterruptedException
é acionada quando um segmento está aguardando ou dormir e outro interrupções de rosca-lo usando ointerrupt
método em sala de aulaThread
. Portanto, se você capturar essa exceção, significa que o encadeamento foi interrompido. Normalmente, não há motivo para ligarThread.currentThread().interrupt();
novamente, a menos que você queira verificar o status "interrompido" do encadeamento de outro lugar.Em relação à sua outra opção de jogar a
RuntimeException
, não parece uma coisa muito sábia a ser feita (quem entenderá isso? Como será tratado?), Mas é difícil dizer mais sem informações adicionais.fonte
Thread.currentThread().interrupt()
define o sinalizador interrompido (novamente), o que é realmente útil se queremos garantir que a interrupção seja notada e processada em um nível superior.Para mim, a principal coisa sobre isso é: uma exceção InterruptedException não está dando errado, é o thread que está fazendo o que você pediu. Portanto, reescrevê-lo em um RuntimeException não faz sentido.
Em muitos casos, faz sentido retroceder uma exceção agrupada em um RuntimeException quando você diz: não sei o que deu errado aqui e não posso fazer nada para corrigi-lo, só quero que ele saia do fluxo de processamento atual e clique em qualquer manipulador de exceções em todo o aplicativo que possua para que possa registrá-lo. Esse não é o caso de uma InterruptedException, é apenas o thread que responde a uma interrupção (), está lançando o InterruptedException para ajudar a cancelar o processamento do thread em tempo hábil.
Portanto, propague a InterruptedException ou coma-a de maneira inteligente (ou seja, em um local em que ela cumpra o que deveria fazer) e redefina o sinalizador de interrupção. Observe que o sinalizador de interrupção é limpo quando a InterruptedException é lançada; a suposição que os desenvolvedores da biblioteca Jdk fazem é que capturar a exceção equivale a manipulá-la, portanto, por padrão, o sinalizador é limpo.
Então, definitivamente, a primeira maneira é melhor, o segundo exemplo postado na pergunta não é útil, a menos que você não espere que o encadeamento seja realmente interrompido, e interrompê-lo equivale a um erro.
Aqui está uma resposta que escrevi descrevendo como as interrupções funcionam, com um exemplo . Você pode ver no código de exemplo onde ele está usando a InterruptedException para salvar um loop while no método runnable.
fonte
A opção padrão correta é adicionar InterruptedException à sua lista de lançamentos. Uma interrupção indica que outro segmento deseja que seu segmento termine. A razão para esta solicitação não é evidenciada e é totalmente contextual; portanto, se você não tiver nenhum conhecimento adicional, deve presumir que é apenas um desligamento amigável e qualquer coisa que evite esse desligamento é uma resposta não amigável.
O Java não lançará aleatoriamente o InterruptedException, todos os conselhos não afetarão seu aplicativo, mas encontrei um caso em que o desenvolvedor que seguiu a estratégia de "engolir" se tornou muito inconveniente. Uma equipe desenvolveu um grande conjunto de testes e usou muito o Thread.Sleep. Agora começamos a executar os testes em nosso servidor de IC e, às vezes, devido a defeitos no código, ficavam presos em esperas permanentes. Para piorar a situação, ao tentar cancelar o trabalho de IC, ele nunca foi fechado porque o Thread.Interrupt destinado a interromper o teste não interrompeu o trabalho. Tivemos que entrar na caixa e matar manualmente os processos.
Para encurtar a história, se você simplesmente lançar a InterruptedException, estará correspondendo à intenção padrão de que seu encadeamento termine. Se você não pode adicionar InterruptedException à sua lista de lançamentos, eu a agruparia em uma RuntimeException.
Há um argumento muito racional a ser feito de que InterruptedException deve ser uma RuntimeException em si, pois isso incentivaria uma melhor manipulação "padrão". Não é uma RuntimeException apenas porque os designers aderiram a uma regra categórica de que uma RuntimeException deve representar um erro no seu código. Como uma InterruptedException não surge diretamente de um erro no seu código, não é. Mas a realidade é que muitas vezes surge uma InterruptedException porque há um erro no seu código (ou seja, loop infinito, dead-lock), e a Interrupção é outro método de outro encadeamento para lidar com esse erro.
Se você sabe que há uma limpeza racional a ser feita, faça-o. Se você conhece uma causa mais profunda da interrupção, pode assumir um tratamento mais abrangente.
Portanto, em resumo, suas opções de manuseio devem seguir esta lista:
fonte
Eu só queria adicionar uma última opção ao que a maioria das pessoas e artigos menciona. Como o mR_fr0g afirmou, é importante lidar com a interrupção corretamente:
Propagando a InterruptException
Restaurar estado de interrupção no encadeamento
Ou adicionalmente:
Não há nada errado em lidar com a interrupção de maneira personalizada, dependendo das circunstâncias. Como uma interrupção é uma solicitação de encerramento, em oposição a um comando poderoso, é perfeitamente válido concluir trabalhos adicionais para permitir que o aplicativo manipule a solicitação normalmente. Por exemplo, se um encadeamento estiver inativo, aguardando resposta de E / S ou de hardware, quando receber a interrupção, será perfeitamente válido fechar normalmente todas as conexões antes de encerrar o encadeamento.
Eu recomendo entender o tópico, mas este artigo é uma boa fonte de informações: http://www.ibm.com/developerworks/java/library/j-jtp05236/
fonte
Eu diria que, em alguns casos, não há problema em não fazer nada. Provavelmente não é algo que você deva fazer por padrão, mas, caso não ocorra uma interrupção, não sei mais o que fazer (provavelmente erro de log, mas isso não afeta o fluxo do programa).
Um caso seria o caso de você ter uma fila de tarefas (bloqueio). Caso você tenha um Thread daemon lidando com essas tarefas e não interrompa o Thread sozinho (que eu saiba, a jvm não interrompe os threads daemon no desligamento da jvm), não vejo como a interrupção ocorra e, portanto, poderia ser apenas ignorado. (Eu sei que um encadeamento daemon pode ser morto pela jvm a qualquer momento e, portanto, é inadequado em alguns casos).
EDIT: Outro caso pode ser um bloco protegido, pelo menos com base no tutorial da Oracle em: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
fonte