Matando o encadeamento após algum limite de tempo especificado em Java

Existe uma maneira de matar um thread filho depois de algum limite de tempo especificado em Java? Edit: Também este segmento específico pode ser bloqueado em seu pior caso (Thread é usado para esperar por uma modificação de arquivo e bloqueia até que este evento ocorra), então eu não tenho certeza que interromper () será bem-sucedido?

Faça uso de ExecutorService para executar o Runnable , checkout os methods em que você pode especificar o tempo limite. Por exemplo

 ExecutorService executor = Executors.newSingleThreadExecutor(); executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes. executor.shutdown(); 

Aqui Task of course implementa Runnable .

Por que não interrupt() após um determinado período? Seu segmento gerado terá que ser capaz de manipular corretamente um InterruptedException .

Veja este artigo ( http://www.javaspecialists.eu/archive/Issue056.html ) para mais informações sobre como encerrar os tópicos de forma limpa.

Veja também a estrutura Executor / Futuro, que fornece methods úteis para coletar resultados e / ou finalizar encadeamentos dentro de limites de tempo específicos.

Não diretamente; Eu acho que a maneira mais simples é join () nesse thread com esse limite de tempo, e interromper o thread se não for feito no momento em que a junit terminar.

Assim,

 Thread t = ... t.join(timelimit); if (t.isAlive) t.interrupt(); 

Repare que eu usei a interrupção em vez de realmente matá-la, é muito mais seguro. Também recomendaria o uso de executores em vez de manipular diretamente os encadeamentos.

Algumas mudanças úteis foram introduzidas como parte do JEP 266 no CompletableFuture desde o Java 9. Usando o método orTimeout , por enquanto, é possível escrever como:

 CompletableFuture.runAsync(thread::run) .orTimeout(30, TimeUnit.SECONDS) .exceptionally(throwable -> { log.error("An error occurred", throwable); return null; }); 

No Java 8, infelizmente, você deve usar algum código extra. Aqui está um exemplo de uso de padrões de delegação com a ajuda do Lombok :

 import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.concurrent.TimeoutException; import static lombok.AccessLevel.PRIVATE; import lombok.AllArgsConstructor; import lombok.experimental.Delegate; @AllArgsConstructor(access = PRIVATE) public class TimeoutableCompletableFuture extends CompletableFuture { public static TimeoutableCompletableFuture runAsync( Runnable runnable) { return new TimeoutableCompletableFuture<>( CompletableFuture.runAsync(runnable)); } @Delegate private final CompletableFuture baseFuture; public TimeoutableCompletableFuture orTimeout(Duration duration) { final CompletableFuture otherFuture = new CompletableFuture<>(); Executors.newScheduledThreadPool( 1, new ThreadFactoryBuilder() .setDaemon(true) .setNameFormat("timeoutable-%d") .build()) .schedule(() -> { TimeoutException ex = new TimeoutException( "Timeout after " + duration); return otherFuture.completeExceptionally(ex); }, duration.toMillis(), MILLISECONDS); return new TimeoutableCompletableFuture<>( baseFuture.applyToEither(otherFuture, a -> a)); } } 

É claro que o código acima pode ser facilmente reescrito como apenas um método de fábrica estático:

 public static CompletableFuture runAsyncOrTimeout( Runnable runnable, long timeout, TimeUnit unit) { CompletableFuture other = new CompletableFuture<>(); Executors.newScheduledThreadPool( 1, new ThreadFactoryBuilder() .setDaemon(true) .setNameFormat("timeoutafter-%d") .build()) .schedule(() -> { TimeoutException ex = new TimeoutException( "Timeout after " + timeout); return other.completeExceptionally(ex); }, timeout, unit); return CompletableFuture.runAsync(runnable).applyToEither(other, a -> a); } 

Você pode usar AOP e uma anotação @Timeable para o seu método de jcabi-aspects (eu sou um desenvolvedor):

 @Timeable(limit = 1, unit = TimeUnit.SECONDS) String load(String resource) { // do something time consuming } 

Quando o limite de tempo é atingido, o seu thread será interrupted() flag como true e é seu trabalho lidar com esta situação corretamente e parar a execução. Normalmente é feito por Thread.sleep(..) .

Matar um segmento geralmente é uma má ideia por motivos relacionados aos documentos da API do Thread .

Se você está morto em matar, use todo um novo processo.

Caso contrário, o habitual é fazer com que o encadeamento System.nanoTime , System.nanoTime um sinalizador (possivelmente volatile ), enfileire uma “poison pill” ou algo dessa natureza.

Brian está certo, interrompê-lo é mais seguro do que “parar” o tópico.
E se o thread estiver travando em um object mid-modification e de repente for parado (o que faz com que o bloqueio seja liberado)? Você obtém resultados estranhos.

Não use destroy() desde que não realize qualquer limpeza.

A maneira mais direta é usar join() , como

 try { thread.join(); } catch (InterruptedException e) {//log exception...} 

Você poderia usar um ExecutorService . Isso faria muito sentido se você tivesse vários segmentos em execução simultaneamente. Se você tiver a necessidade de gerar novos encadeamentos enquanto outros encadeamentos estiverem em execução, você poderá combinar isso com um BlockingQueue .

Um ThreadPoolExecutor (uma implementação ExecutorService ) pode ter um BlockingQueue como argumento e você pode simplesmente adicionar novos threads à fila. Quando você terminar, simplesmente termine o ThreadPoolExecutor .

 private BlockingQueue queue; ... ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, new Long(1000), TimeUnit.MILLISECONDS, this.queue); 

Você pode manter uma contagem de todos os segmentos adicionados à fila. Quando você pensa que está feito (a fila está vazia, talvez?) Simplesmente compare isso com

  if (issuedThreads == pool.getCompletedTaskCount()) { pool.shutdown(); } 

Se os dois combinarem, você está feito. Outra maneira de terminar o pool é aguardar um segundo em um loop:

 try { while (!this.pool.awaitTermination(1000, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) {//log exception...}