Лучший способ реализовать TimeoutTask

Я пытаюсь реализовать TimeoutTask, который завершится по истечении заданного тайм-аута. Вот что у меня есть:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class TimeoutTask {

    private Long timeout;
    private TimeUnit unit;

    public TimeoutTask(Long timeout, TimeUnit unit) {
        this.timeout = timeout;
        this.unit = unit;
    }

    public <T> T execute(Callable<T> callable) {
        Objects.requireNonNull(timeout, "Timeout");
        Objects.requireNonNull(unit, "Time Unit");
        Objects.requireNonNull(callable, "Callable");

        ExecutorService service = 
                Executors.newFixedThreadPool(1);
        FutureTask<T> task = new FutureTask<T>(callable);
        service.execute(task);

        try {
            return task.get(timeout, unit);
        } catch (InterruptedException | ExecutionException | TimeoutException cause) {
            if(cause instanceof TimeoutException) {
                System.out.println("\nTimeout occured.");
                task.cancel(true);
            }
        } finally {
            System.out.println("Finally called.");
            service.shutdownNow();
        }

        return null;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        Callable<String> callable = () -> {         
            System.out.print("Enter something: ");
            BufferedReader consoleReader = 
                    new BufferedReader(new InputStreamReader(System.in));
            String str = consoleReader.readLine();
            return str;
        };

        TimeoutTask timeoutTask = new TimeoutTask(Long.valueOf(10), TimeUnit.SECONDS);
        timeoutTask.execute(callable);  
    }
}

Он работает, и я вижу сообщение о тайм-ауте, если ввод не вводится. Однако процесс не останавливается. Ниже приведен снимок экрана с консоли Eclipse, см. Выделенную часть:

введите описание изображения здесь

Когда я ввожу какие-либо данные, процесс немедленно прекращается. Я вызываю FutureTask#cancel из блока catch, а также из блока finally. Я прошу выключить службу, сделав вызов ExecutorService#shoutDownNow, и я вижу, что он печатает, что наконец вызывается.

  • Как я могу остановить выполнение процесса в случае тайм-аута?
  • Есть ли лучший подход для этого?

Я использую Java 8.

Ссылка:

Обновлять

В ответе Марко Топольник сказал, что причина не в прерванный Java IO; поэтому я меняю Callable как:

Callable<Long> callable = () -> {           
    long i = 0;
    for(; i <Long.MAX_VALUE;i++){

    }
    return i;
};

В этом случае произошло то же самое.

Примечание. Callable - это всего лишь образец.


person Tapas Bose    schedule 16.09.2014    source источник


Ответы (2)


Ты звонишь

String str = consoleReader.readLine();

Это означает, что вы используете классический, блокирующий, непрерывный ввод-вывод Java. Метод продолжает блокироваться даже после истечения времени ожидания, и shutdownNow() тоже не может его коснуться, потому что он также просто пытается прервать поток.

Кстати, я запустил ваш код, и на выходе показано, что наконец-то вызвано.

Обновлять

Ваш обновленный Callable так же бесперебойен, как и исходный. Вы должны либо вызвать метод, объявляющий выброс InterruptedException (например, Thread.sleep()), либо проверить Thread.interrupted() себя в цикле.

person Marko Topolnik    schedule 16.09.2014
comment
Спасибо, сэр, за ваш ответ, я подумал о том же, поэтому я изменил Callable, как показано в моем обновлении. В этом случае произошло то же самое. - person Tapas Bose; 16.09.2014
comment
Да Thread.interrupted() работал в шлейфе. Таким образом, не существует универсального способа реализации TimeoutTask, для которого вызывающей стороне этого класса не нужно было бы думать об остановке Thread! - person Tapas Bose; 16.09.2014
comment
Совершенно верно, и это сделано намеренно: только кооперативный механизм прерывания дает вам возможность реализовать безопасную очистку. - person Marko Topolnik; 16.09.2014

Проблема, которую необходимо решить, заключается в том, что System.in.read() не реагирует на прерывания. Узнайте, как обойти это, в информационном бюллетене Хайнца М. Кабуца

person alonana    schedule 16.09.2014