Как заставить FutureTask возвращаться после исключения TimeoutException?

В приведенном ниже коде я перехватываю TimeoutException через 100 секунд, как и предполагалось. В этот момент я ожидаю, что код выйдет из main, а программа завершится, но она продолжит вывод на консоль. Как заставить задачу прекратить выполнение после тайм-аута?

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;
    }


}

person deltanovember    schedule 15.08.2009    source источник


Ответы (3)


Вам нужно отменить свою задачу по тайм-ауту (и прервать ее поток). Вот для чего нужен метод 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;
}
person Alexey Romanov    schedule 15.08.2009
comment
!task.cancelled() должно быть !isCancelled(), как первоначально написал djna - person Zed; 15.08.2009
comment
Я думаю, что гораздо лучше использовать прерывания. Во-первых, вашему вызываемому коду вообще не нужно знать о task. Во-вторых, когда вы используете различные блокирующие операции в вызываемом коде (например, Thread.sleep()), они не будут реагировать на task.isCancelled(), но обычно реагируют на прерывания. Таким образом, использование cancel(true) и оповещение вашего кода о прерываниях обычно является лучшим способом сделать это. (Ваш код также будет более общим, потому что механизм прерываний так широко используется в Java) - person Peter Štibraný; 15.08.2009
comment
Я полагаю, моя точка зрения заключается в том, что Прерывание — это кооперативный механизм. (ibm.com/developerworks/java/library/j-jtp05236.html ) - person Peter Štibraný; 15.08.2009
comment
@Питер Хорошая статья! Я изменил ответ соответственно. - person Alexey Romanov; 15.08.2009
comment
код Cleary не тестировался. task будет неопределенным в блоке catch - person mtk; 14.10.2019

Ваш 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) и используйте его.

person Peter Štibraný    schedule 15.08.2009

После того, как вы поймали TimeoutException, вам нужно вызвать метод отмены (true) вашей задачи...

или выключите свой ExecutorService, вызвав shutdownNow()...

или выйдите из виртуальной машины, вызвав System.exit(0)

в зависимости от ваших потребностей

person Zed    schedule 15.08.2009