Handling InterruptedException in Java
Qual é a diferença entre as seguintes formas de manipulação InterruptedException
? Qual é a melhor maneira de o fazer?
try{
//...
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
ou
try{
//...
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
EDIT: também gostaria de saber em que cenários são usados estes dois.
7 answers
Em primeiro lugar, você deve ver {[[4]} para o que é: uma parte da assinatura do método e um possível resultado de chamar o método que você está chamando. Então comece por abraçar o fato de que um InterruptedException
é um resultado perfeitamente válido do método call.
Faz sentido para o método que estás a implementar para lançar um?Dito de outra forma, será um resultado sensato ao chamar o seu método?
-
Se sim , então
throws InterruptedException
deve fazer parte de a assinatura do seu método, e deve deixar a excepção propagar-se (ou seja, não a apanhe de todo).Exemplo: O seu método espera por um valor da rede para terminar o cálculo e retornar um resultado. Se a chamada de rede de bloqueio lançar um
InterruptedException
o seu método não consegue terminar a computação de uma forma normal. Deixaste oInterruptedException
propagar-se.int computeSum(Server server) throws InterruptedException { // Any InterruptedException thrown below is propagated int a = server.getValueA(); int b = server.getValueB(); return a + b; }
-
Se não, então você não deve declarar o seu método com
throws InterruptedException
e você deve (deve!) apanhar a excepção. Agora duas coisas são importantes para ter em mente nesta situação:Alguém interrompeu o teu fio. Esse alguém provavelmente está ansioso para cancelar a operação, terminar o programa graciosamente, ou o que quer que seja. Devias ser educado com essa pessoa e voltar do teu método sem mais demoras.
Mesmo que o seu método consiga produzir um valor de retorno sensato no caso de um
InterruptedException
o facto de o fio ter sido interrompido pode ainda ser importante. Em particular, o código que chama o seu método pode estar interessado em saber se uma interrupção ocorreu durante a execução do seu método. Você deve, portanto, registar o facto de ter ocorrido uma interrupção, assinalando a interrupção:Thread.currentThread().interrupt()
Exemplo: O utilizador pediu para imprimir uma soma de dois valores. A impressão "
Failed to compute sum
" é aceitável se a soma não puder ser calculada (e muito melhor do que deixar o programa estoirar com um traço de pilha devido a umInterruptedException
). Por outras palavras, não faz sentido declarar este método comthrows InterruptedException
.void printSum(Server server) { try { int sum = computeSum(server); System.out.println("Sum: " + sum); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag System.out.println("Failed to compute sum"); } }
Outros exemplos:
Execução
Runnable
: Como você pode ter descoberto, a assinatura deRunnable.run
não permite a derrubadaInterruptedExceptions
. Bem, Você inscreveu-se na implementaçãoRunnable
, o que significa que Você se inscreveu para lidar com possívelInterruptedExceptions
. Escolha uma interface diferente, comoCallable
, ou seguir a segunda abordagem acima.
A chamar
Thread.sleep
: está a tentar ler um ficheiro e o espectrómetro diz que deve tentar 10 vezes com um segundo no meio. Liga-me. Então, tens de lidar com oInterruptedException
. Para um método comotryToReadFile
faz todo o sentido dizer, " Se sou interrompido, não posso completar a minha acção de tentar ler o file " . Por outras palavras, faz todo o sentido que o método lance.String tryToReadFile(File f) throws InterruptedException { for (int i = 0; i < 10; i++) { if (f.exists()) return readFile(f); Thread.sleep(1000); } return null; }
Este post foi reescrito como um artigo aqui.
Propagar a
InterruptedException
- Declare o seu método de jogar o verificado {[[0]} para que o seu chamador tem que lidar com ele.Repor a interrupção - por vezes não consegue atirar
InterruptedException
. Nestes casos, deve apanhar o {[[0]} e restaurar o interromper o estado chamando o métodointerrupt()
NocurrentThread
para que o código mais alto da pilha de chamadas possa ver que foi emitida uma interrupção.
O {[[0]} é lançado quando um fio está à espera ou a dormir e outro fio interrompe-o usando o método interrupt
na classe Thread
. Então, se você pegar esta exceção, significa que o fio foi interrompido. Normalmente não faz sentido voltar a chamar Thread.currentThread().interrupt();
, a menos que queira verificar o estado "interrompido" do tópico de outro lugar.
Em relação à tua outra opção de lançar um RuntimeException
, não parece uma coisa muito sábia de fazer (quem vais apanhar isto? como será tratado?) mas é difícil dizer mais sem informações adicionais.
Para mim a coisa chave sobre isso é: uma interrupção da percepção não é nada que vai errado, é o tópico fazendo o que você lhe disse para fazer. Por isso, derrubá-lo embrulhado numa conceção rústica não faz sentido.
Em muitos casos, faz sentido repensar uma excepção embrulhada numa ideia errónea quando dizes que não sei o que correu mal aqui e não posso fazer nada para o corrigir, só quero que saia do fluxo de processamento actual e bata em qualquer excepção à escala da aplicação. tenho um encarregado para o registar. Esse não é o caso com uma interrupção da percepção, é apenas o tópico que responde a ter a interrupção() chamada sobre ela, está lançando a interrupção da percepção, a fim de ajudar a cancelar o processamento da thread em tempo hábil.Então propaga a interrupção da percepção, ou come-a inteligentemente (ou seja, num lugar onde terá realizado o que estava destinado a fazer) e repõe a bandeira de interrupção. Note que a bandeira de interrupção é limpa quando o O conceito interrompido é jogado; a suposição que os desenvolvedores da Biblioteca Jdk fazem é que pegar a exceção equivale a lidar com ela, então por padrão a bandeira é limpa.
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 tópico seja realmente interrompido, e interrompê-lo equivale a um erro.
Eis uma resposta que escrevi descrevendo como as interrupções funcionam, com um exemplo . Você pode ver no exemplo de código onde ele está usando o conceito interrompido para sair de um laço while no método de execução Runnable.
A escolha por omissão correcta é adicionar uma excepção interrompida à sua lista de lances. Uma interrupção indica que outro tópico deseja que o seu tópico termine. A razão para este pedido não é evidente e é inteiramente contextual, então se você não tem nenhum conhecimento adicional você deve assumir que é apenas um desligamento amigável, e qualquer coisa que evite esse desligamento é uma resposta não amigável.
O Java não irá lançar aleatoriamente os Interruptedexception's, todos os conselhos não irão afectar a sua aplicação mas eu encontrei um caso em que o desenvolvedor está seguindo a estratégia "swallow" tornou-se muito inconveniente. Uma equipe desenvolveu um grande conjunto de testes e usou Thread.Dorme muito. Agora começamos a executar os testes em nosso servidor CI, e às vezes devido a defeitos no código ficaria preso em espera permanente. Para piorar a situação, ao tentar cancelar o trabalho de CI nunca fechou porque o tópico.Interromper que foi destinado a abortar o teste não abortou o trabalho. Nós tínhamos para entrar na caixa e matar manualmente os processos.
Resumindo, se simplesmente lançar a ideia interrompida, estará a corresponder à intenção predefinida que o seu tópico deverá terminar. Se não puder adicionar uma suspensão à sua lista de lançamento, eu embrulhá-la-ia numa solução rápida.
Há um argumento muito racional a ser feito de que a interrupção da excepção deve ser uma sugestão de execução em si, uma vez que isso encorajaria um melhor tratamento "padrão". Não é uma ideia errada. só porque os designers se agarraram a uma regra categórica de que uma percepção Runtimeexpressiva deve representar um erro no seu código. Uma vez que uma interrupção da percepção não surge diretamente de um erro em seu código, não é. Mas a realidade é que muitas vezes surge uma percepção interrompida porque há um erro em seu código, (ou seja, laço infinito, bloqueio morto), e a interrupção é o método de algum outro thread para lidar com esse erro.
Se sabes que há uma limpeza racional a fazer, fá-lo. Se você conhece uma causa mais profunda para a interrupção, você pode assumir um tratamento mais abrangente.Por isso, em resumo, as suas escolhas de manipulação devem seguir esta lista:
- por omissão, adicione aos lançamentos.
- Se não for permitido adicionar aos arremessos, lance o RuntimeException (e). (melhor escolha de várias opções más)
- só quando se conhece uma causa explícita da interrupção, manejar como desejado. Se a sua manipulação é local para o seu método, em seguida, reiniciado interrompido por uma chamada roscar.currentThread().interromper().
Propagação da interrupção
Repor o estado de interrupção no tópico
Ou adicionalmente:
- Tratamento Personalizado da interrupção
Não há nada de errado em lidar com a interrupção de uma forma personalizada, dependendo das suas circunstâncias. Como um interromper é um pedido de terminação, ao contrário de um comando forçoso, é perfeitamente válido para completar o trabalho adicional para permitir que a aplicação para lidar com o pedido graciosamente. Por exemplo, se um fio está dormindo, esperando IO ou uma resposta de hardware, quando recebe a interrupção, então é perfeitamente válido para graciosamente fechar quaisquer conexões antes de terminar o fio.
Eu recomendo entender o tópico, mas este artigo é uma boa fonte de informação.: http://www.ibm.com/developerworks/java/library/j-jtp05236/Um caso seria no caso de ter uma fila de Tarefas (bloqueio). No caso de ter um tópico do daemon a lidar com estas tarefas e de não interromper o tópico por si próprio (tanto quanto sei, o jvm não interrompe o daemon threads on jvm shutdown), I see no way for the interrupt to happen, and therefore it could be just ignored. (Eu sei que um fio de daemon pode ser morto pela jvm a qualquer momento e, portanto, são inadequados em alguns casos).
Editar: Outro caso pode ser guardado blocos, pelo menos com base no tutorial do Oracle em: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html