В чем разница между Future и FutureTask в Java?

Поскольку использование ExecutorService может submit выполнить задачу Callable и вернуть Future, зачем использовать FutureTask для переноса задачи Callable и использовать метод execute? Я чувствую, что они оба делают одно и то же.


person Hesey    schedule 10.02.2011    source источник


Ответы (6)


На самом деле вы правы. Эти два подхода идентичны. Как правило, вам не нужно обертывать их самостоятельно. Если да, то вы, вероятно, дублируете код в AbstractExecutorService:

/**
 * Returns a <tt>RunnableFuture</tt> for the given callable task.
 *
 * @param callable the callable task being wrapped
 * @return a <tt>RunnableFuture</tt> which when run will call the
 * underlying callable and which, as a <tt>Future</tt>, will yield
 * the callable's result as its result and provide for
 * cancellation of the underlying task.
 * @since 1.6
 */
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

Единственная разница между Future и RunnableFuture заключается в методе run():

/**
 * A {@link Future} that is {@link Runnable}. Successful execution of
 * the <tt>run</tt> method causes completion of the <tt>Future</tt>
 * and allows access to its results.
 * @see FutureTask
 * @see Executor
 * @since 1.6
 * @author Doug Lea
 * @param <V> The result type returned by this Future's <tt>get</tt> method
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

Хорошая причина, по которой Executor может построить FutureTask для вас, состоит в том, чтобы убедиться, что не существует более одной ссылки на экземпляр FutureTask. То есть Исполнитель владеет этим экземпляром.

person Mark Renouf    schedule 10.02.2011
comment
FutureTask.get() никогда не генерирует исключение CancellationException, в отличие от Future.get(). Это правильно? См. docs.oracle .com/javase/1.5.0/docs/api/java/util/concurrent/, java.util.concurrent.TimeUnit). - person Chris Morris; 01.03.2013

FutureTask Этот класс предоставляет base implementation of Future, с методами для запуска и отмены вычисления

Future — это интерфейс

person fmucar    schedule 10.02.2011

Future это просто интерфейс. За кулисами реализация FutureTask.

Вы можете использовать FutureTask вручную, но вы потеряете преимущества использования Executor (объединение потока, ограничение потока и т. д.). Использование FutureTask очень похоже на использование старого Thread и использование метода run.

person nanda    schedule 10.02.2011
comment
FutureTask реализует как Future‹V›, так и Runnable, так почему же его нельзя отправить в ExecutorService? - person Ji ZHANG; 30.09.2013
comment
Я думаю, что FutureTask можно отправить в Executor? FutureTask можно использовать для переноса объекта Callable или Runnable. Поскольку FutureTask реализует Runnable, FutureTask может быть отправлен исполнителю для выполнения. docs.oracle.com/javase/7/ документы/api/java/util/concurrent/ - person Vikki; 29.08.2018

Вам нужно будет использовать FutureTask только в том случае, если вы хотите изменить его поведение или получить доступ к его Callable позже. В 99% случаев используйте Callable и Future.

person Peter Lawrey    schedule 10.02.2011

Поскольку Марк и другие правильно ответили, что Future - это интерфейс для FutureTask, а Executor фактически его фабрика; это означает, что код приложения редко создает экземпляр FutureTask напрямую. Чтобы дополнить обсуждение, я привожу пример, показывающий ситуацию, когда FutureTask создается и используется напрямую, вне любого Executor:

    FutureTask<Integer> task = new FutureTask<Integer>(()-> {
        System.out.println("Pretend that something complicated is computed");
        Thread.sleep(1000);
        return 42;
    });

    Thread t1 = new Thread(()->{
        try {
            int r = task.get();
            System.out.println("Result is " + r);
        } catch (InterruptedException | ExecutionException e) {}
    });
    Thread t2 = new Thread(()->{
        try {
            int r = task.get();
            System.out.println("Result is " + r);
        } catch (InterruptedException | ExecutionException e) {}
    });
    Thread t3 = new Thread(()->{
        try {
            int r = task.get();
            System.out.println("Result is " + r);
        } catch (InterruptedException | ExecutionException e) {}
    });

    System.out.println("Several threads are going to wait until computations is ready");
    t1.start();
    t2.start();
    t3.start();
    task.run(); // let the main thread to compute the value

Здесь FutureTask используется как инструмент синхронизации, как CountdownLatch или аналогичный барьерный примитив. Его можно было реализовать заново, используя CountdownLatch или блокировки и условия; FutureTask просто делает его хорошо инкапсулированным, не требующим пояснений, элегантным и с меньшим количеством кода.

Также обратите внимание, что метод FutureTask#run() должен вызываться явно в любом из потоков; рядом нет Исполнителя, чтобы сделать это за вас. В моем коде он в конечном итоге выполняется основным потоком, но можно изменить метод get() для вызова run() в первом потоке, вызывающем get(), поэтому первый поток, достигший get(), и это может быть любой из T1, T2 или T3, будет делать расчет для всех остальных потоков.

На этой идее - первый поток, запрашивающий результат, будет выполнять вычисления для других, в то время как одновременные попытки будут заблокированы - основан Memoizer, см. пример Memoizer Cache на странице 108 в "Параллелизм Java на практике".

person Espinosa    schedule 27.06.2017

Как уже было сказано, но если говорить не в общем, а в более технических терминах, так как FutureTask реализует RunnableFuture, то вызывать его можно с помощью

FutureTask<T> result = new FutureTask<T>(new #YourClassImplementingCallable());
Thread t1= new Thread(result);
t1.start();
Object<T> obj = result.get();

Это больше соответствует старому runnable, но также имеет возможность возвращать результат через обратный вызов.

Большая сила FutureTask по сравнению с Future заключается в том, что он имеет больший контроль над потоками, а не просто отправляет вызываемый объект в Future и позволяет исполнителю обрабатывать потоки.

как вы можете вызвать здесь t1.join().

person nik_7    schedule 22.05.2021