Futuros en Java, Parte 2: Interfaz Future

Continuo con la serie de post sobre Futuros en Java que comencé con el anterior post. En este hablaré sobre la interfaz Future añadida en java 5.

Esta serie constará inicialmente de las siguientes partes:
Parte 1: Introducción
Parte 2: interfaz Future
Parte 3: CompletableFuture, introducción
Parte 4: CompletableFuture, uso avanzado

Interfaz Future: uso y ejemplos

Para poder evitar los bloqueos de los que hablé en el anterior post, nuestra primera herramienta en Java es la interfaz Future. Esta fue añadida hace tiempo ya, con Java 1.5, y nos provee lo básico para el trabajo con futuros. Como veremos, demasiado básico… Vamos con algunos ejemplos para demostrar su funcionamiento.

Creación:
No podemos crear directamente un futuro. Es una interfaz, y nos lo devolverá, por ejemplo, un método.

        Future<String> future = someBigProcess.processVeryLong("test");

Tiene un parámetro genérico que será el tipo del objeto que esperamos obtener cuando el futuro se complete.

Para crearlo, no hay una manera directa. Por ejemplo, la más sencilla sería esta:

    private final ExecutorService executor = Executors.newFixedThreadPool(5);

    public Future<String> processVeryLong(String param1) throws InterruptedException {
        return executor.submit(() -> {
            TimeUnit.SECONDS.sleep(5);
            LOG.info("Terminando processVeryLong...");
            return param1.concat(" result");
        });
    }

Usamos la interfaz ExecutorService para «lanzar» un Callable (con un lambda de Java 8). Para eso necesitamos haber definido el Executor, que es una clase que se encarga de gestionar pool de threads. Su método ‘submit’ ejecuta en otro thread el Callable o Runnable recibido, y devuelve un Future, que, cuando se complete la ejecución, contendrá el resultado.

Uso y bloqueos:
El uso principal de la interfaz Future es su método ‘get’:

        String result = future.get();

Este método es el que obtiene el valor real del futuro. Si el futuro todavía no se ha completado, al llamar a este método nos quedaremos bloqueados hasta que se complete.
Ojo, si al completarse el futuro se genera una excepción, la llamada a este método es la que lanzará esa excepción («envuelta» en una ExecutionException).
También existe una versión del mismo método que recibe un timeout, para no quedarnos bloqueados eternamente:

        String result = future.get(5, TimeUnit.SECONDS);

Aparte tiene algunos métodos más:
cancel(boolean mayInterruptIfRunning) -> Cancela la ejecución del futuro.
isCancelled() -> Comprueba si ha sido cancelado.
isDone() -> Comprueba si el futuro se ha completado.

Pero no nos ofrece ninguna posibilidad más. Si usamos la interfaz Future, en algún momento, si o si, tendremos que bloquear nuestro thread para obtener el resultado.

La interfaz Future de java se queda muy corta para siquiera empezar a montar un sistema «reactivo». Nos ofrece una manera sencilla de ejecutar métodos de manera asíncrona, pero nada más.

En el próximo post de la serie veremos como la clase CompletableFuture, añadida en Java 8, nos ayuda a resolver esto.

Tenéis en mi github (proyecto future-example) un ejemplo completo con la interfaz Future, con un test sencillo con el que probarlo, tanto para un caso normal, como para un caso que lanza una excepción.