8 Future¶
La interfaz future¶
La interfaz Future<V>
representa una tarea asíncrona enviada a un ejecutor, cuyo resultado será obtenido en un momento futuro. En otras palabras, corresponde a una promesa de valor. Se trata de una interfaz parametrizable, cuyo tipo V
corresponderá al tipo del valor retornado por la tarea.
Veamos la definición de la interfaz Future<V>
:
public interface Future<V> {
// Métodos relacionados con la obtención del valor retornado
// por la tarea asíncrona.
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
// Métodos relacionados con la cancelación de la tarea asíncrona.
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
// Métodos relacionados con la completitud de la tarea asíncrona.
boolean isDone();
}
Como vemos, cuando el hilo que envío la tarea asíncrona al ejecutor desee acceder al valor que ésta haya retornado podrá llamar al método get()
del objeto Future<V>
que fue obtenido en respuesta al envío de la tarea al ejecutor.
Si cuando llamamos al método get()
la tarea correspondiente ya ha terminado su ejecución correctamente, directamente se nos retornará el valor del tipo V
que haya retornado la tarea. Si la tarea había sido cancelada, se nos lanzará la excepción CancellationException
.
Si, por el contrario, para cuando llamamos al método get()
la tarea correspondiente aún no ha terminado de ejecutarse, el hilo que llama al método get()
quedará bloqueado en espera de que la tarea termine su ejecución y retorne un valor del tipo V
. Cuando la tarea concluya el valor retornado por ésta será almacenado en el Future
y se reactivará el hilo que fue bloqueado retornándole el valor correspondiente. Si estando bloqueado el hilo que llamó a get()
éste es interrumpido, se le lanzará la excepción InterruptedException
.
El método get(timeout, timeUnit)
está sobrecargado de manera que podamos pasarle el tiempo máximo que queremos que el hilo esté esperando a que termine de ejecutarse la tarea correspondiente. Si transcurrido dicho tiempo la tarea no ha finalizado su ejecución, el método get(time, timeUnit)
lanzará la excepción TimeoutException
.
Finalmente, si la tarea correspondiente terminó su ejecución lanzando una excepción, cuando llamemos al método get()
del objeto Future
correspondiente, se lanzará la excepción ExecutionException
, indicativa que en la tarea ejecutada en el ejecutor se produjo una excepción.
Otra de las funcionalidades que nos proporciona la interfaz Future
, a parte de obtener el valor retornado por la tarea, es la poder cancelar la tarea si ya no estamos interesados en que se ejecute o termine su ejecución. Para ello, tenemos disponible el método cancel(mayInterruptIfRunning)
, que trata de marcar la tarea asociada como cancelada, retornando si se consiguió marcar o no. Así, pueden darse los siguientes casos:
- Si la tarea ya había concluido su ejecución, ya sea correctamente o con una excepción, evidentemente no podrá ser marcada como cancelada, porque ya concluyó su ejecución, por lo que el método
cancel(mayInterruptIfRunning)
retornaráfalse
. - De igual forma, si la tarea ya había sido cancelada con anterioridad, no podrá ser cancelada de nuevo, en cuyo caso también se retornará
false
. - Si la tarea estaba en el ejecutor esperando poder comenzar su ejecución, se marcará como cancelada, la tarea nunca llegará a ejecutarse, y se retornará
true
. - Si la tarea estaba en ejecución en el ejecutor para cuando se llama al método
cancel(mayInterruptIfRunning)
, se marcará como cancelada y se retornarátrue
. Además, si el parámetromayInterruptIfRunning
recibe el valortrue
entonces el hilo del threadpool del ejecutor en el que se está ejecutando la tarea será marcado como interrumpido. Dependerá ya del código de la propia tarea detectar que el hilo ha sido interrumpido y finalizar su ejecución. Como vemos la cancelación real es colaborativa, es decir, que requiere de la colaboración de la propia tarea. Si no pasamos el valortrue
paramayInterruptIfRunning
o la tarea no hace caso al hecho de que el hilo en el que se está ejecutando haya sido marcado para interrupción, la tarea completará su ejecución.
Debemos tener en cuenta que una tarea ha sido marcada como cancelada, si llamamos al método get()
del objeto para obtener su valor Future
correspondiente se lanzará la excepción CancellationException
.
Si queremos saber si la tarea asociada a un objeto Future
ha sido marcada como cancelada podemos llamar a su método isCancelled()
.
Finalmente, la interfaz Future
proporciona el método isDone()
, que retorna true
si la tarea asociada terminó su ejecución, ya sea correctamente, lanzando una excepción o porque no llegó a ejecutarse al ser cancelada antes de poder iniciar su ejecución.