Как мне заставить FutureTask вернуться после TimeoutException?
-
16-09-2019 - |
Вопрос
В приведенном ниже коде я улавливаю исключение TimeoutException через 100 секунд, как и предполагалось.На этом этапе я ожидаю, что код выйдет из основного режима и программа завершится, но он продолжает печатать на консоль.Как мне заставить задачу прекратить выполнение после тайм-аута?
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;
}
}
Решение
Вам необходимо отменить задачу по тайм-ауту (и прервать ее поток).Это то что cancel(true)
метод предназначен для.:
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;
}
Другие советы
Ваш Callable должен иметь возможность быстро остановиться, когда это необходимо.
Ваш код:
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;
}
Уже могу сделать это благодаря звонку Thread.sleep()
.Дело в том, что futureTask.cancel(true)
прервет другой поток, и ваш код должен отреагировать на это прерывание. Thread.sleep()
делает это.Если вы не использовали Thread.sleep()
или другой код прерываемой блокировки, вам придется проверить Thread.currentThread().isInterrupted()
самостоятельно и как можно скорее прекратите работу (например,бросая new InterruptedException()
), когда вы обнаружите, что это правда.
Вам нужно позвонить futureTask.cancel(true);
из вашего обработчика исключений, чтобы отменить и прервать поток, который запускает вашу задачу.
Мой совет — узнать о механизме прерывания (это отличная статья: Работа с InterruptedException), и используйте его.
Как только вы поймали TimeoutException, вам нужно вызвать метод cancel(true) вашей задачи...
или завершите работу службы ExecutorService, вызвав ShutdownNow()...
или выйдите из виртуальной машины, вызвав System.exit(0)
в зависимости от ваших потребностей