|
|
< Day Day Up > |
|
10.6 Callable Tasks and Future ResultsExecutors in general operate on tasks, which are objects that implement the Runnable interface. In order to provide more control over tasks, Java also defines a special runnable object known as a callable task: package java.util.concurrent;
public interface Callable<V> {
public <V> call( ) throws Execption;
}Unlike a runnable object, a callable object can return a result or throw a checked exception. Callable objects are used only by executor services (not simple executors); the services operate on callable objects by invoking their call() method and keeping track of the results of those calls. When you ask an executor service to run a callable object, the service returns a Future object that allows you to retrieve those results, monitor the status of the task, and cancel the task. The Future interface looks like this: public interface Future<V> {
V get( ) throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
boolean isDone( );
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled( );
}Callable and future objects have a one-to-one correspondence: every callable object that is sent to an executor service returns a matching future object. The get() method of the future object returns the results of its corresponding call( ) method. The get() method blocks until the call() method has returned (or until the optional timeout has expired). If the call() method throws an exception, the get() method throws an ExecutionException with an embedded cause, which is the exception thrown by the call( ) method. The future object keeps track of the state of an embedded Callable object. The state is set to cancelled when the cancel() method is called. When the call() method of a callable task is called, the call() method checks the state: if the state is cancelled, the call() method immediately returns. When the cancel() method is called, the corresponding callable object may be in one of three states. It may be waiting for execution, in which case its state is set to cancelled and the call() method is never executed. It may have completed execution, in which case the cancel( ) method has no effect. The object may be in the process of running. In that case, if the mayInterruptIfRunning flag is false, the cancel() method again has no effect. If the mayInterruptIfRunning flag is true, however, the thread running the callable object is interrupted. The callable object must still pay attention to this, periodically calling the Thread.interrupted() method to see if it should exit. When an object in a thread pool is cancelled, there is no immediate effect: the object still remains queued for execution. When the thread pool is about to execute the object, it checks the object's internal state, sees that it has been cancelled, and skips execution of the object. So, cancelling an object on a thread pool queue does not immediately make space in the thread pool's queue. Future calls to the execute() method may still be rejected, even though cancelled objects are on the thread pool's queue: the execute() method does not check the queue for cancelled objects. One way to deal with this situation is to call the purge() method on the thread pool. The purge() method looks over the entire queue and removes any cancelled objects. One caveat applies: if a second thread attempts to add something to the pool (using the execute() method) at the same time the first thread is attempting to purge the queue, the attempt to purge the queue fails and the canceled objects remain in the queue. A better way to cancel objects with thread pools is to use the remove() method of the thread pool, which immediately removes the task from the thread pool queue. The remove() method can be used with standard runnable objects. 10.6.1 The FutureTask ClassYou can associate a Runnable object with a future result using the FutureTask class: public class FutureTask<V> implements Future<V>, Runnable {}This class is used internally by the executor service: the object returned from the submit() method of an executor service is an instance of this class. However, you can use this class directly in programs as well. This makes sense when you need to monitor the status of a runnable object within an executor: you can construct a future task with an embedded runnable object and send the future task to the execute() method of an executor (or an executor service). You can then use the methods of the Future interface to monitor the status of the run() method of the embedded runnable object. A FutureTask object can hold either an embedded runnable or callable object, depending on which constructor is used to instantiate the object: public FutureTask(Callable<V> task); public FutureTask(Runnable task, V result); The get() method of a future task that embeds a callable task returns whatever is returned by the call( ) method of that embedded object. The get() method of a future task that embeds a runnable object returns whatever object was used to construct the future task object itself. We use this class in our next example and also in our examples in Chapter 15. |
|
|
< Day Day Up > |
|