A "Callable" is similar to a "Runnable", but it returns a value. The "Callable" interface is a parameterized type, with a single method "call".
public interface Callable<V> {
V call() throws Exception;
}
The type parameter is the type of the returned value.
A "Future" holds the "result" of an asynchronous computation. You can start a computation, give someone the "Future" object, and forget about it. The owner of the "Future" object can obtain the result when it is ready.
The "Future" interface has the following methods:
public interface Future<V> {
V get() throws ...;
V get(long timeout, TimeUnit unit) throws ...;
void cancel(boolean mayInterrupted);
boolean isCancel();
boolean isDone();
}
A call to the first "get" method blocks until the computation is finished. The second "get" method throws a "TimeoutException" if the call timed out before the computation finished. If the thread running the computation is interrupted, both methods throw an "InterruptedException". If the computation has already finished, "get" returns immediately.
You can cancel the computation with the "cancel" method. If the computation has not yet started, it is canceled and will never start. If the computation is currently in prograss, it is interrupted if the "mayInterrupted" parameter is "true".
The "FutureTask" wrapper is a convenient mechanism for turning a "Callable" into both a "Future" and a "Runnable" -- it implements both interfaces. For example:
Callable<Integer> myComputation = ...;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
Thread t = new Thread(task); // it's a "Runnable"
t.start();
...
Integer result = task.get(); // it's a "Future"
Executors:
The "Executors" class has a number of static factory methods for constructing thread pool:
newCachedThreadPool New threads are created as needed; idle threads are kept for 60 seconds.
newFixedThreadPool The pool contains a fixed set of threads; idle threads are kept indefinitely.
newSingleThreadExecutor A "pool" with a single thread that executes the submitted tasks sequentially
newScheduledThreadPool A fixed-thread pool for scheduled execution; a replacement for "java.util.Timer".
newSingleThreadScheduledExecutor A single-thread pool for scheduled execution.
Thread Pools:
Above methods return an object of the "ThreadPoolExecutor" class that implements the "ExecutorService" interface. You can submit a "Runnable" or "Callable" to the "ExecutorService" with one of the following methods:
Future<?> submit(Runnable task)
Future<T> submit(Runnable task, T result)
Future<T> submit(Callable<T> task)
The pool will run the submitted task at its earlist convenience. When you call "submit", you get back a "Future" object that you can use to query the state of the task. The first "submit" method returns a odd-looking "Future<?>". You can use such an object to call "isDone", "cancel", or "isCancelled", but the "get" method simply return "null" upon completion. The second version of "submit" also submit a "Runnable", and the "get" method of the "Future" returns the given "result" object upon completion. The third version submits a "Callable", and the returned "Future" gets the result of the computation when it is ready.
When you are done with a thread pool, call "shutdown". This method initiates the shutdown sequence for the pool. An executor that is shut down accepts no new tasks. When all tasks are finished, the the threads in the pool die. Alternatively, you can call "shutdownNow". The pool then cancels all tasks that have not yet begun and attempts to interrupt the running threads. The "ScheduledExecutorService" interface has methods for sheduled or repeated execution of tasks. It is a generalization of "java.util.Timer" that allows for thread pooling. The "newScheduledThreadPool" and "newSingleThreadScheduledExecutor" methods of the "Executors" class return objects that implement the "ScheduledExecutorService" interface.
ScheduledFuture<V> schedule(Callable task, long time, TimeUnit unit)
ScheduledFuture<V> schedule(Runnable task, long time, TimeUnit unit)
schedules the task after the given time has elapsed
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
schedules the given task periodicallly, every "period" units, after the initial delay has elapsed.
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit)
schedule the given task to run periodically, with "delay" units
between completion of one invocation and the start of the next, after the initial delay has elapsed.
Controlling Groups of Tasks:
Sometimes, an executor is used for a more tactical reason, simply to controp a group of related threads. For example, you can cancel all tasks in an executor with the "shutdownNow" method. The "invokeAny" method submits all objects in a collection of "Callable" objects and returns the result of a completed task. You don't know which task that is -- presumably, it is the one that finished most quickly. Use this method for a search problem in which you are willing to accept any solution. The "invokeAll" method submits all object in a collection of "Callable" objects, blocks until all of them complete, and returns a list of "Future" objects that represent the solutions to all tasks. You can process the results of the computation when they are available, like this:
List<Callable<T>> tasks = ...;
List<Future<T>> results = executor.invokeAll(tasks);
for (Future<T> result : results) {
processFuture(result.get());
}
A disadvantage of this approach is that you may wait needlessly if the first task happens to take a long time. It would make more sense to obtain the results in the order in which they are available. This can be arranged with the "ExecutorCompletionService". Start with an executor, obtained in the usual way. Then construct an "ExecutorCompletionService". Submit tasks to the "ExecutorCompletionService". The "ExecutorCompletionService" manages a blocking queue of "Future" objects, containing the results of the submitted tasks as they become available. Thus, a more efficient organization for the preceding computation is the following:
ExecutorCompletionService<T> service = new ExecutorCompletionSerive<T>(executor);
for (Callable<T> task : tasks) {
service.submit(task);
}
for (int i = 0; i < tasks.size(); i++) {
processFuture(service.take().get());
}