Prefácio
Para interruptedException, uma maneira comum de lidar com isso é "engolir" - pegá -lo e não fazer nada (ou gravar, mas isso não é muito melhor) - assim como listar 4 mais tarde. Infelizmente, essa abordagem ignora o fato de que as interrupções podem ocorrer durante esse período, e as interrupções podem fazer com que o aplicativo perca a capacidade de cancelar a atividade ou fechá -la no tempo.
Método de bloqueio
Quando um método lança uma interrupção deception, ele não apenas diz que ele pode lançar uma exceção de verificação específica, mas também informa outra coisa. Por exemplo, diz que é um método de bloqueio, que tentará eliminar o bloqueio e retornar o mais cedo possível se você responder corretamente.
O método de bloqueio é diferente do método geral que leva muito tempo para ser executado. A conclusão de um método geral depende apenas do que está fazendo e se há recursos de computação suficientes disponíveis (ciclos da CPU e memória). A conclusão do método de bloqueio também depende de alguns eventos externos, como expiração do timer, conclusão de E/S ou ações de outro thread (solte um bloqueio, defina um sinalizador ou coloque uma tarefa em uma fila de trabalho). Os métodos gerais terminam após o trabalho, enquanto os métodos de bloqueio são mais difíceis de prever porque dependem de eventos externos. Os métodos de bloqueio podem afetar a capacidade de resposta, pois é difícil prever quando eles terminarão.
O método de bloqueio pode não ser encerrado porque não pode esperar o evento que está esperando, por isso é muito útil tornar o método de bloqueio cancelável (e geralmente é muito útil se os métodos de não bloqueio de longa duração forem canceláveis). Uma operação cancelável refere -se a uma operação que pode ser encerrada de fora antes da conclusão normal. O mecanismo de interrupção fornecido por thread e suportado por thread.sleep () e object.wait () é um mecanismo de cancelamento; Ele permite que um thread solicite que outro thread pare o que está fazendo. Quando um método lança uma interrupção, está lhe dizendo que, se o thread que executa o método for interrompido, ele tentará parar o que está fazendo e retornar com antecedência e, ao lançar uma interrupção, ele retorna com antecedência. Um método de biblioteca de bloqueio bem-comportado deve responder às interrupções e lançar uma interrupção para que possa ser usada em atividades canceláveis sem afetar a resposta.
Interrupção do thread
Cada encadeamento possui um atributo booleano associado a ele, que representa o status interrompido do thread. O estado de interrupção é falso no início; Quando outro thread interrompe um thread chamando Thread.Interrup (), uma das duas situações ocorre. Se esse thread estiver executando um método de bloqueio interrompível de baixo nível, como Thread.Sleep (), Thread.Join () ou Object.wait (), ele desbloqueará e lançará uma interrupção. Caso contrário, interromp () apenas define o estado de interrupção do thread. Depois que o código em execução no encadeamento interrompido pode pesquisar o estado de interrupção para ver se é solicitado para interromper o que está fazendo. O estado de interrupção pode ser lido por thread.isinterrupted () e pode ser lido e liberado por uma operação chamada Thread.Interrupted ().
A interrupção é um mecanismo colaborativo. Quando um thread interrompe outro thread, o thread interrompido não interrompe necessariamente o que está fazendo imediatamente. Em vez disso, uma interrupção é uma solicitação educada para outro thread para interromper o que está fazendo quando está disposto e conveniente. Alguns métodos, como thread.sleep (), levam essas solicitações muito a sério, mas cada método não responde necessariamente às interrupções. Para solicitações de interrupção, os métodos que não bloqueiam, mas ainda demoram muito tempo para executar podem pesquisar o status de interrupção e retornar com antecedência quando interrompidos. Você pode ignorar solicitações de interrupção à vontade, mas isso afetará a resposta.
Um benefício da natureza colaborativa das interrupções é que ele fornece maior flexibilidade para construir com segurança atividades canceláveis. Raramente queremos que uma atividade pare imediatamente; Se a atividade for cancelada enquanto uma atualização estiver em andamento, a estrutura de dados do programa poderá ser inconsistente. A interrupção permite que uma atividade cancelável limpe o trabalho contínuo, restaure os invariantes, notifique outras atividades que ela deve ser cancelada antes de ser encerrada.
Manipular interruptedException
Se jogar uma interrupção de exceção significa que um método é um método de bloqueio, chamar um método de bloqueio significa que seu método também é um método de bloqueio e você deve ter algum tipo de estratégia para lidar com a InterrupteDException. A estratégia mais fácil é geralmente lançar uma exceção interrompida, conforme mostrado no código nos métodos Puttask () e GetTask () na Listagem 1. Fazer isso faz com que o método responda às interrupções e simplesmente adicione uma cláusula de interrupção à cláusula de arremessos.
Listagem 1. Não pegando interrompedException, propagando -a ao chamador
classe pública TaskQueue {private estático final int max_tasks = 1000; Private BlockingQueue <Task> fila = new LinkedBlockingQueue <Teck> (max_tasks); public void puttask (tarefa r) lança interruptedException {fileue.put (r); } public tarefa getTask () lança interruptedException {return fileue.take (); }} Às vezes, é necessário algum trabalho de limpeza antes que a exceção seja propagada. Nesse caso, você pode pegar a interrupção, executar uma limpeza e depois lançar uma exceção. A Listagem 2 demonstra essa técnica, um mecanismo usado para combinar jogadores em serviços de jogos on -line. O método MatchPlayers () espera que dois jogadores cheguem e depois iniciem um novo jogo. Se o método for interrompido quando um jogador chegar, mas outro jogador não chegou, ele colocará esse jogador de volta na fila e novamente interrompida, para que o pedido do jogador para o jogo não seja perdido.
Listagem 2. Execute o trabalho de limpeza específico da tarefa antes de re-arruinar o interruptedException
classe pública Playermatcher {Private Playersource Players; public Playermatcher (jogadores do Playersource) {this.Players = Players; } public void matchplayers () lança interruptedException {try {player playerone, playertwo; while (true) {playerone = playertwo = null; // Aguarde dois jogadores chegarem e inicie um novo jogo PlayerOne = Players.waitForPlayer (); // pode jogar ie playertwo = players.waitforplayer (); // pode jogar o IE StartNewGame (PlayerOne, Playertwo); }} catch (interruptedException e) {// Se tivermos um jogador e fomos interrompidos, coloque o jogador de volta se (playerone! = null) players.addfirst (playneone); // então propagar o arremesso de exceção e; }}} Não pare de engolir vivo
Às vezes, lançar uma interrupção não é apropriada, por exemplo, quando uma tarefa definida por uma execução chama um método interrompível. Nesse caso, a InterruptedException não pode ser re-ampliada, mas você também não quer fazer nada. Quando um método de bloqueio detecta uma interrupção e lança uma interrupção, ele limpa o estado de interrupção. Se uma interrupção é capturada, mas não puder ser re-ampliada, a evidência da interrupção ocorre deve ser mantida para que o código de nível superior na pilha de chamadas possa conhecer a interrupção e responder a ela. Essa tarefa pode ser realizada chamando interrupção () para "reinterrunder" o thread atual, conforme mostrado na Listagem 3. Pelo menos, sempre que uma interrupção é capturada e não é re-arruinada, o thread atual é reinterrupto antes de retornar.
Listagem 3. Retomando o estado interrompido após capturar interruptedException
classe pública TaskRunner implementa Runnable {Private BlockingQueue <kky> fila; public TaskRunner (BlockingQueue <Task> fila) {this.queue = fila; } public void run () {try {while) {tarefa de tarefa = fileue.take (10, timeUnit.seconds); tarefa.Execute (); }} catch (interruptEdException e) {// Restaure o thread de status interrompido.CurrentThread (). interrupt (); }}} A pior coisa a se fazer ao lidar com a InterrupteDException é engoli-la cru-pegue-a e depois não o re-lotou nem novamente o estado de interrupção do thread. Para uma exceção com a qual você não sabe lidar, a maneira mais padrão de lidar é pegá-lo e gravá-lo, mas esse método ainda é como uma interrupção bruta, porque o código de nível mais alto na pilha de chamadas ainda não pode obter informações sobre a exceção. (Não é aconselhável apenas gravar interruptedException, porque é tarde demais para processá -lo quando alguém vem ler o log.) Listagem 4 mostra um padrão amplamente usado, que também é um padrão de interrupção de deglutição crua:
Lista 4. Interrupção de deglutição bruta - não faça isso
// Não faça esta classe pública TaskRunner implementa Runnable {Private BlockingQueue <ket> fila; public TaskRunner (BlockingQueue <Task> fila) {this.queue = fila; } public void run () {try {while) {tarefa de tarefa = fileue.take (10, timeUnit.seconds); tarefa.Execute (); }} catch (interruptedException Swallowed) { / * Não faça isso - restaure o status interrompido em vez de * /}}} Se a InterruptedException não puder ser re-ampliada, independentemente de você planejar processar a solicitação de interrupção ou não, você ainda precisará reinterrutar o thread atual, pois uma solicitação de interrupção pode ter vários "receptores". O pool de threads padrão (ThreadPoolExector) a implementação do thread é responsável pela interrupção, portanto, interromper uma tarefa em um pool de threads em execução pode ter um efeito duplo. Um é cancelar a tarefa e o outro é notificar o segmento de execução que o pool de threads está prestes a ser fechado. Se a tarefa devorar a solicitação, o tópico do trabalhador não saberá que há uma interrupção solicitada, atrasando o desligamento do aplicativo ou serviço.
Implementar tarefas canceladas
Não há semântica específica para interrupções na especificação do idioma, mas em programas maiores, é difícil manter qualquer semântica de interrupção, exceto o cancelamento. Dependendo da atividade, os usuários podem solicitar cancelamento por meio de uma GUI ou por meio de um mecanismo de rede, como JMX ou Serviço da Web. A lógica do programa também pode ser solicitada para cancelar. Por exemplo, um rastreador da web se desligará automaticamente se detectar que o disco está cheio; caso contrário, um algoritmo paralelo iniciará vários threads para procurar diferentes áreas do espaço da solução e cancelar esses threads quando um dos threads encontrar uma solução.
Só porque uma tarefa é cancelável não significa que uma solicitação de interrupção precisa ser respondida imediatamente. Para tarefas que executam o código em um loop, geralmente é necessário apenas verificar as interrupções uma vez para cada iteração do loop. Dependendo de quanto tempo o loop é executado, pode levar algum tempo para qualquer código perceber que o thread foi interrompido (faça uma pesquisa do estado de interrupção chamando o thread.isinterrupted () ou chame um método de bloqueio). Se a tarefa precisar ser responsiva, poderá pesquisar o estado de interrupção com mais frequência. O método de bloqueio geralmente pesquisa o estado de interrupção imediatamente na entrada e, se estiver definido para melhorar a capacidade de resposta, também lança uma interrupção de exceção.
A única vez que você pode parar de engolir vivo é que você sabe que o tópico está prestes a sair. Esse cenário ocorre apenas quando a classe que chama o método interrompível faz parte do encadeamento, não executável ou código da biblioteca geral, como mostrado na Listagem 5. A Listagem 5 cria um thread que lista os números primos até que seja interrompido, o que também pode sair quando é interrompido. O loop costumava procurar verificações do Primes para interrupções em dois lugares: um é pesquisar o método ISInterrupted () na cabeça do loop while, e o outro é chamar o método de bloqueio BlockingQueue.put ().
Listagem 5. Se você souber que o tópico está prestes a sair, você pode engolir a interrupção
classe pública PrimeProducer estende o thread {private final BlockingQueue <figinteger> fila; PrimeProducer (BlockingQueue <FigInteger> fila) {this.queue = fila; } public void run () {try {biginteger p = biginteger.one; while (! thread.currentThread (). isInterrupted ()) fileue.put (p = p.NextProbablePrime ()); } catch (interruptedException consumido) { / * Permitir que o thread saia * /}} public void cancel () {interrupt (); }} Método de bloqueio inquebrável
Nem todos os métodos de bloqueio lançam interruptedException. O bloco Classes de fluxo de entrada e saída aguardam a conclusão da E/S, mas eles não lançam interrupções e não retornarão com antecedência se interrompidas. No entanto, para a E/S do soquete, se um thread fechar o soquete, a operação de E/S bloqueadora nesse soquete terminará mais cedo e uma demora será lançada. As classes de E/S não bloqueando em java.nio não suportam E/S interrompíveis, mas também podem ser desbloqueadas fechando o canal ou solicitando o despertar no seletor. Da mesma forma, tentar adquirir um bloqueio interno (entrando em um bloco sincronizado) não pode ser interrompido, mas o ReentrantLock suporta o modo de aquisição interrompível.
Tarefas incansáveis
Algumas tarefas se recusam a ser interrompidas, o que as torna não abolidas. No entanto, mesmo tarefas não canceláveis devem tentar preservar o estado de interrupção, caso o código de nível superior na pilha de chamadas precise processar interrupções após o término das tarefas não canceláveis. A Listagem 6 mostra um método que aguarda uma fila de bloqueio até que um item disponível apareça na fila, independentemente de ser interrompido ou não. Por conveniência, retoma o estado de interrupção após o final em um bloco finalmente, para não privar o chamador da solicitação de interrupção. (Ele não pode retomar o estado de interrupção anteriormente, porque isso causará um loop infinito - blockingqueue.take () pesquisará imediatamente o estado de interrupção na entrada e, se o conjunto de Estado de interrupção for encontrado, uma interrupção será lançada.)
Listagem 6. Tarefas não ligáveis que restauram o estado interrompido antes de retornar
Tarefa pública GetNextTask (BlockingQueue <Task> fila) {boolean interromped = false; tente {while (true) {tente {return fileue.take (); } catch (interruptedException e) {interromped = true; // cair e tentar novamente}}} finalmente {if (interrompido) thread.currentThread (). interrupt (); }} Resumir
Você pode usar o mecanismo de interrupção de colaboração fornecido pela plataforma Java para construir estratégias flexíveis de cancelamento. As atividades podem decidir, a seu próprio critério, se são canceláveis ou não canceláveis e como responder a interrupções e também podem adiar interrupções se o retorno imediato comprometer a integridade do aplicativo. Mesmo que você queira ignorar completamente as interrupções no seu código, verifique se o estado de interrupção é restaurado sem o refúgio, para que o código que a chama não saiba o que a interrupção está acontecendo. O exposto acima é o conteúdo completo da teoria e prática de Java de lidar com exceções interrompidas de exceção. Espero que este artigo seja útil para você. Se você tiver alguma dúvida, deixe uma mensagem para discussão.