Since use ExecutorService can submit a Callable task and return a Future, why need to use FutureTask to wrap
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 task = new FutureTask(()-> {
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".