Eu quero executar um thread por um período fixo de tempo. Se não for concluído nesse período, quero matá-lo, lançar alguma exceção ou lidar com isso de alguma forma. Como pode ser feito?
Uma maneira de fazer isso, como descobri neste thread, é usar um TimerTask dentro do método run () do Thread.
Existem soluções melhores para isso?
Edição: Adicionando uma recompensa como eu precisava de uma resposta mais clara. O código ExecutorService fornecido abaixo não soluciona meu problema. Por que devo dormir () após a execução (algum código - não tenho controle sobre esse trecho de código)? Se o código for concluído e o sleep () for interrompido, como pode ser um tempo limite?
A tarefa que precisa ser executada não está no meu controle. Pode ser qualquer parte do código. O problema é que esse pedaço de código pode ter um loop infinito. Eu não quero que isso aconteça. Então, eu só quero executar essa tarefa em um thread separado. O encadeamento pai precisa aguardar até que o encadeamento termine e precise saber o status da tarefa (ou seja, se atingiu o tempo limite ou se ocorreu alguma exceção ou se é um sucesso). Se a tarefa entrar em um loop infinito, meu encadeamento pai continuará aguardando indefinidamente, o que não é uma situação ideal.
fonte
sleep()
foi apenas um esboço para representar "uma tarefa em execução há muito tempo". Apenas substituí-lo com a sua tarefa real;)interrupt()
chamadas em seu encadeamento ... nem todas as chamadas "bloqueadas" o fazem, como tentei apontar na minha resposta. As especificidades da tarefa que você está tentando abortar fazem uma enorme diferença na abordagem que deve ser usada. Mais informações sobre a tarefa seriam úteis.Respostas:
De fato, em
ExecutorService
vez de usar em vez deTimer
, aqui está um SSCCE :Jogue um pouco com o
timeout
argumento doFuture#get()
método, por exemplo, aumente para 5 e você verá que o thread termina. Você pode interceptar o tempo limite nocatch (TimeoutException e)
bloco.Atualização: para esclarecer um mal-entendido conceitual, não
sleep()
é necessário. É usado apenas para fins de demonstração / SSCCE. Basta fazer sua tarefa de longa duração ali no lugar de . Dentro da sua tarefa de longa execução, você deve verificar se o encadeamento não é interrompido da seguinte maneira:sleep()
fonte
Thread.sleep(4000)
por outra declaração de longa duração e o exemplo não funcionará. Em outras palavras, este exemplo funcionaria apenas se oTask
fosse projetado para entender aThread.isInterrupted()
mudança de status.Não existe uma maneira 100% confiável de fazer isso para qualquer tarefa antiga. A tarefa deve ser escrita com essa capacidade em mente.
Bibliotecas Java principais, como
ExecutorService
cancelam tarefas assíncronas cominterrupt()
chamadas no thread de trabalho. Portanto, por exemplo, se a tarefa contiver algum tipo de loop, você deverá verificar seu status de interrupção em cada iteração. Se a tarefa estiver executando operações de E / S, elas também devem ser interrompidas - e a configuração pode ser complicada. De qualquer forma, lembre-se de que o código deve verificar ativamente se há interrupções; definir uma interrupção não necessariamente faz nada.Obviamente, se sua tarefa for um loop simples, basta verificar o tempo atual em cada iteração e desistir quando um tempo limite especificado tiver decorrido. Um segmento de trabalho não é necessário nesse caso.
fonte
execute()
método da interface principalExecutor
pode executar uma tarefa no thread de chamada. Não há declaração semelhante para ossubmit()
métodos dessas instâncias deExecutorService
retornoFuture
. A implicação do serviço é que existem threads de trabalho que precisam ser limpos por meio do encerramento e que as tarefas são executadas de forma assíncrona. Dito isto, não há nada no contrato que diga queExecutorService
é proibido executar tarefas no encadeamento de envio; essas garantias vêm das APIs de implementação, comoExecutors
fábricas.Considere usar uma instância do ExecutorService . Ambos
invokeAll()
einvokeAny()
métodos estão disponíveis com umtimeout
parâmetro.O encadeamento atual será bloqueado até que o método seja concluído (não tenho certeza se isso é desejável), porque as tarefas foram concluídas normalmente ou o tempo limite foi atingido. Você pode inspecionar o
Future
(s) retornado (s) para determinar o que aconteceu.fonte
Supondo que o código do thread esteja fora de seu controle:
A partir da documentação Java mencionada acima:
Bottom Line:
Certifique-se de que todos os threads possam ser interrompidos, ou então você precisa de conhecimentos específicos sobre o thread - como ter um sinalizador para definir. Talvez você possa exigir que a tarefa seja dada junto com o código necessário para interrompê-la - defina uma interface com um
stop()
método. Você também pode avisar quando não conseguiu parar uma tarefa.fonte
BalusC disse:
Mas se você substituir
Thread.sleep(4000);
porfor (int i = 0; i < 5E8; i++) {}
, ele não será compilado, porque o loop vazio não gera umInterruptedException
.E para que o thread seja interrompível, ele precisa lançar um
InterruptedException
.Isso parece ser um problema sério para mim. Não vejo como adaptar essa resposta para trabalhar com uma tarefa geral de longa duração.
Editado para adicionar: Eu avaliei isso como uma nova pergunta: [ interrompendo um encadeamento após um tempo fixo, ele precisa lançar InterruptedException? ]
fonte
Eu acho que você deve dar uma olhada nos mecanismos adequados de manipulação de simultaneidade (threads executando em loops infinitos não soam bem por si só). Certifique-se de ler um pouco sobre o tópico Threads "matar" ou "parar" .
O que você está descrevendo parece muito com um "encontro", então você pode dar uma olhada no CyclicBarrier .
Pode haver outras construções (como usar CountDownLatch, por exemplo) que podem resolver seu problema (um encadeamento aguardando um tempo limite para a trava, o outro deve fazer a contagem regressiva da trava se tiver feito seu trabalho, o que liberaria seu primeiro encadeamento após um tempo limite ou quando a contagem regressiva da trava for chamada).
Normalmente, recomendo dois livros nesta área: Programação Simultânea em Java e Java Simultaneidade na Prática .
fonte
Eu criei uma classe auxiliar apenas para isso há algum tempo. Funciona bem:
É chamado assim:
fonte
Publico um código que mostra uma maneira de resolver o problema. Como exemplo, estou lendo um arquivo. Você pode usar esse método para outra operação, mas precisa implementar o método kill () para que a operação principal seja interrompida.
espero que ajude
Saudações
fonte
Aqui está minha classe auxiliar realmente simples de usar para executar ou chamar parte do código Java :-)
Isso se baseia na excelente resposta da BalusC
fonte
O fragmento a seguir iniciará uma operação em um encadeamento separado e aguarde até 10 segundos para que a operação seja concluída. Se a operação não for concluída a tempo, o código tentará cancelar a operação e continue em seu caminho alegre. Mesmo que a operação não possa ser cancelada facilmente, o encadeamento pai não esperará o término do encadeamento filho.
O
getExecutorService()
método pode ser implementado de várias maneiras. Se você não possui nenhum requisito específico, pode simplesmente solicitar oExecutors.newCachedThreadPool()
pool de threads sem limite superior no número de threads.fonte
SomeClass
eFuture
?Uma coisa que eu não vi mencionado é que matar tópicos geralmente é uma má ideia. Existem técnicas para tornar os métodos de encadeamento abortáveis de maneira limpa , mas isso é diferente de simplesmente matar um encadeamento após um tempo limite.
O risco com o que você está sugerindo é que você provavelmente não sabe em que estado o thread estará quando o matar - então corre o risco de introduzir instabilidade. Uma solução melhor é garantir que seu código encadeado não se desligue ou responda bem a uma solicitação de cancelamento.
fonte
Ótima resposta de BalusC:
mas Apenas para adicionar que o tempo limite em si não interrompe o próprio thread. mesmo se você estiver verificando com while (! Thread.interrupted ()) em sua tarefa. se você quiser garantir que o encadeamento seja interrompido, verifique se future.cancel () será chamado quando a exceção de tempo limite for capturada.
fonte
Eu acho que a resposta depende principalmente da tarefa em si.
Se a primeira resposta for sim e a segunda for não, você pode mantê-la simples assim:
Se isso não for uma opção, reduza seus requisitos - ou mostre algum código.
fonte
Eu estava procurando por um ExecutorService que possa interromper todos os Runnables expirados, mas não encontrou nenhum. Depois de algumas horas, criei um como abaixo. Esta classe pode ser modificada para aumentar a robustez.
Uso:
fonte
Agora, encontro uma questão como esta. Isso acontece para decodificar imagem. O processo de decodificação leva muito tempo para que a tela fique preta. l adiciono um controle de tempo: quando o tempo for muito longo, então apareça no Thread atual. O seguinte é o diff:
fonte
Eu tive o mesmo problema. Então, eu vim com uma solução simples como esta.
Garante que, se o bloco não for executado dentro do prazo. o processo será encerrado e lançará uma exceção.
exemplo:
fonte
Na solução fornecida pelo BalusC , o thread principal permanecerá bloqueado pelo período de tempo limite. Se você tiver um conjunto de encadeamentos com mais de um encadeamento, precisará do mesmo número de encadeamentos adicionais que usarão a chamada de bloqueio Future.get (timeout longo, unidade TimeUnit) para aguardar e fechar o encadeamento se exceder o período de tempo limite.
Uma solução genérica para esse problema é criar um Decorador ThreadPoolExecutor que possa adicionar a funcionalidade de tempo limite. Essa classe Decorator deve criar quantos threads o ThreadPoolExecutor possui e todos esses threads devem ser usados apenas para aguardar e fechar o ThreadPoolExecutor.
A classe genérica deve ser implementada como abaixo:
O decorador acima pode ser usado como abaixo:
fonte