Como faço para obter FutureTask para retornar após TimeoutException?
-
16-09-2019 - |
Pergunta
No código abaixo, eu estou pegando um TimeoutException após 100 segundos como pretendido. Neste ponto, eu esperaria o código para sair do principal e o programa para terminar, mas ele mantém a impressão para o console. Como faço para obter a tarefa de parar a execução após o tempo limite?
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
private static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
FutureTask<T> task = new FutureTask<T>(c);
THREAD_POOL.execute(task);
return task.get(timeout, timeUnit);
}
public static void main(String[] args) {
try {
int returnCode = timedCall(new Callable<Integer>() {
public Integer call() throws Exception {
for (int i=0; i < 1000000; i++) {
System.out.println(new java.util.Date());
Thread.sleep(1000);
}
return 0;
}
}, 100, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
Solução
Você precisa cancelar a sua tarefa no tempo limite (e interromper seu segmento). Isso é o que método cancel(true)
é para. :
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
private static <T> T timedCall(FutureTask<T> task, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
THREAD_POOL.execute(task);
return task.get(timeout, timeUnit);
}
public static void main(String[] args) {
try {
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
public Integer call() throws Exception {
for (int i=0; i < 1000000; i++) {
if (Thread.interrupted()) return 1;
System.out.println(new java.util.Date());
Thread.sleep(1000);
}
return 0;
}
});
int returnCode = timedCall(task, 100, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
task.cancel(true);
}
return;
}
Outras dicas
Seu deve mobilizável para ser capaz de parar rapidamente, quando necessário.
O seu código:
public Integer call() throws Exception {
for (int i=0; i < 1000000 && !task.cancelled(); i++) {
System.out.println(new java.util.Date());
Thread.sleep(1000); // throws InterruptedException when thread is interrupted
}
return 0;
}
já é capaz de fazer isso graças a chamar Thread.sleep()
. Ponto é que futureTask.cancel(true)
vai interromper outro segmento, e seu código precisa reagir a esta interrupção. Thread.sleep()
faz isso. Se você não usar Thread.sleep()
ou outro código de bloqueio de interrupção, você teria que verificar Thread.currentThread().isInterrupted()
por si mesmo, e sair o mais rápido possível (por exemplo, jogando new InterruptedException()
) quando você achar que isso seja verdade.
Você precisa chamar futureTask.cancel(true);
do seu manipulador de exceção para cancelar e linha de interrupção que corre a sua tarefa.
O meu conselho é para aprender sobre mecanismo de interrupção (isso é ótimo artigo: Lidar com InterruptedException ), e usá-lo.
Uma vez que você pegou o TimeoutException, você precisa chamar o método cancel (true) de sua tarefa ...
ou desligar o ExecutorService chamando shutdownNow () ...
ou sair do VM chamando System.exit (0)
dependendo de suas necessidades