52

Since use ExecutorService can submit a Callable task and return a Future, why need to use FutureTask to wrap Callable task and use the method execute? I feel they both do the same thing.

Hesey
  • 4,307
  • 5
  • 28
  • 31

6 Answers6

44

FutureTask This class provides a base implementation of Future, with methods to start and cancel a computation

Future is the interface

Ravindra babu
  • 42,401
  • 8
  • 208
  • 194
fmucar
  • 13,463
  • 2
  • 42
  • 50
26

In fact you are correct. The two approaches are identical. You generally don't need to wrap them yourself. If you are, you're likely duplicating the code in 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);
}

The only difference between Future and RunnableFuture, is the run() method:

/**
 * 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();
}

A good reason to let the Executor construct the FutureTask for you is to ensure that there is no possible way more than one reference exists to the FutureTask instance. That is, the Executor owns this instance.

Mark Renouf
  • 29,573
  • 19
  • 89
  • 120
  • FutureTask.get() never throws a CancellationException, whereas Future.get() does. Is this correct? See http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/FutureTask.html#get(long, java.util.concurrent.TimeUnit). – Chris Morris Mar 01 '13 at 19:56
16

Future is just interface. Behind the scene, the implementation is FutureTask.

You can absolutely use FutureTask manually but you will lose the advantages of using Executor (pooling thread, limit the thread, etc). Using FutureTask is quite similar to using the old Thread and using the run method.

eebbesen
  • 4,760
  • 7
  • 46
  • 67
nanda
  • 23,828
  • 12
  • 65
  • 90
  • 1
    FutureTask implements both Future and Runnable so why can't it be submitted to ExecutorService? – Ji ZHANG Sep 30 '13 at 11:19
  • I think FutureTask can be submitted to Executor? "A FutureTask can be used to wrap a Callable or Runnable object. Because FutureTask implements Runnable, a FutureTask can be submitted to an Executor for execution." https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/FutureTask.html – Vikki Aug 29 '18 at 07:11
7

You would only need to use FutureTask if you want to change its behaviour or access its Callable later. For 99% of uses, just use Callable and Future.

Peter Lawrey
  • 498,481
  • 72
  • 700
  • 1,075
4

As Mark, and others, correctly answered that Future is the interface for FutureTask and Executor effectively its factory; meaning that application code rarely instantiates FutureTask directly. To complement the discussion I am providing an example showing a situation where FutureTask is constructed and used directly, outside any 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

Here, FutureTask is used as a synchronization tool, like CountdownLatch or similar barrier primitive. It could have been re-implemented by using CountdownLatch or locks and conditions; FutureTask just makes it nicely encapsulated, self-explanatory, elegant and with less code.

Also note that FutureTask#run() method must be called explicitly, in any of the threads; there no Executor around to do it for you. In my code, it is eventually executed by the main thread, but one can modify get() method to call run() on the first thread calling get(), therefore the first thread reaching get(), and it can be any of T1, T2 or T3, would do the calculation for all remaining threads.

On this idea - first thread requesting result would do the calculation for others, while concurrent attempts would be blocked - is based Memoizer, see Memoizer Cache example from page 108 in "Java Concurrency in Practice".

Espinosa
  • 2,132
  • 18
  • 22
0

As already mentioned , but speaking not in general but in more of technical terms, since FutureTask implements RunnableFuture, you can call it using

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

This is more inline with older runnable but it also has the capacity to return result via callback.

The greater power of FutureTask over Future lies in the fact that it has more control over threads as against just simply Submitting a callable to Future and let executor handle the threads.

like you can call here t1.join().

nik_7
  • 127
  • 1
  • 5