Реализация будущего интерфейса для общих вычислений


Я реализую интерфейс Future<Collection<Integer>>, чтобы разделить результат некоторого массового вычисления между всеми потоками в приложении.

На самом деле, я намеревался просто поместить экземпляр класса implemetting Future<Collection<Integer>> в объект ApplicationScope так, чтобы любой другой поток, которому нужен результат, просто попросил Future из object и вызвал метод get() на нем, поэтому используя вычисление, выполненное каким-то другим потоком.

Мой вопрос заключается в реализации Метод cancel. Сейчас я бы написал что-то вроде этого:

public class CustomerFutureImpl implements Future<Collection<Integer>>{

    private Thread computationThread;
    private boolean started;
    private boolean cancelled;
    private Collection<Integer> computationResult;

    private boolean cancel(boolean mayInterruptIfRunning){
        if( computationResult != null )
            return false;
        if( !started ){
            cancelled = true;
            return true;
        } else {
            if(mayInterruptIfRunning)
                 computationThread.interrupt();
        }
    }

    //The rest of the methods
}

Но реализация метода не удовлетворяет документации будущего , потому что нам нужно бросить CancellationException в любой поток, ожидающий результата (вызвал метод get()).

Должен ли я добавить еще одно поле, например private Collection<Thread> waitingForTheResultThreads;, а затем прервать каждый поток из Collection, поймать прерванное исключение, а затем throw new CancellationException()?

Дело в том, что такое решение кажется мне несколько странным... Нет в этом я уверен.
1 2

1 ответ:

Вообще следует избегать реализации Future напрямую. Код параллелизма очень трудно получить правильно, а фреймворки для распределенного выполнения-особенно ExecutorService - обеспечит Future экземпляры, ссылающиеся на единицы работы, о которых вы заботитесь.

Возможно, вы уже знаете об этом и намеренно создаете новый подобный сервис, но я считаю важным напомнить, что для подавляющего большинства случаев использования вам не нужно определять свой собственный Future реализация.

Вы можете посмотреть на инструменты параллелизма, предоставляемые Guava, в частности ListenableFuture, который является подинтерфейсом Future, предоставляющим дополнительные функции.


Предполагая, что вы действительно хотите определить пользовательский тип Future, используйте Guava AbstractFuture реализация как отправная точка, так что вам не придется заново изобретать сложные детали, с которыми вы сталкиваетесь.

На ваш конкретный вопрос, если вы посмотрите на осуществление AbstractFuture.get(), Вы увидите, что он реализован с циклом while, который ищет value, чтобы стать ненулевым, в это время он вызывает getDoneValue(), который либо возвращает значение, либо вызывает CancellationException. Таким образом, по существу, каждый поток, блокирующий вызов Future.get(), опрашивает поле Future.value каждый раз и вызывает CancellationException, если он обнаруживает, что Future был отменен. Нет необходимости отслеживать Collection<Thread> или что-либо в этом роде, так как каждый поток может проверять состояние из Future независимо, и вернуть или бросить по мере необходимости.