Clases Avanzadas Callable Future Y Completablefuture

Clases Avanzadas: Callable, Future y CompletableFuture

En el desarrollo de aplicaciones concurrentes en Java, las clases Callable, Future y CompletableFuture proporcionan herramientas avanzadas para manejar tareas asincrónicas, controlar su ejecución y gestionar sus resultados. Este documento explora cómo funcionan estas clases, sus diferencias y cómo utilizarlas de manera efectiva.


1. Introducción a Callable

Callable es una interfaz funcional en Java que representa una tarea que devuelve un resultado y puede lanzar excepciones comprobadas. Es similar a Runnable, pero con las siguientes diferencias clave:

  • Devuelve un resultado de tipo genérico T.
  • Puede lanzar excepciones comprobadas.

Definición de la interfaz Callable

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Ejemplo de uso de Callable

import java.util.concurrent.Callable;

public class EjemploCallable {
    public static void main(String[] args) throws Exception {
        Callable<String> tarea = () -> {
            Thread.sleep(1000); // Simula trabajo
            return "Resultado de la tarea";
        };

        String resultado = tarea.call();
        System.out.println(resultado);
    }
}

2. Introducción a Future

Future es una interfaz que representa el resultado de una tarea asincrónica. Se utiliza junto con ExecutorService para manejar tareas concurrentes.

Principales métodos de Future

  • get(): Bloquea hasta que el resultado está disponible.
  • get(long timeout, TimeUnit unit): Bloquea hasta que el resultado está disponible o el tiempo de espera expira.
  • cancel(boolean mayInterruptIfRunning): Intenta cancelar la tarea.
  • isDone(): Verifica si la tarea ha terminado.
  • isCancelled(): Verifica si la tarea fue cancelada.

Ejemplo de uso de Future

import java.util.concurrent.*;

public class EjemploFuture {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        Callable<String> tarea = () -> {
            Thread.sleep(2000); // Simula trabajo
            return "Resultado de Future";
        };

        Future<String> future = executor.submit(tarea);

        try {
            System.out.println("Esperando el resultado...");
            String resultado = future.get();
            System.out.println("Resultado: " + resultado);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

3. Introducción a CompletableFuture

CompletableFuture es una implementación avanzada de Future que permite construir flujos de trabajo asincrónicos y reactivos. Proporciona una API rica para manejar tareas sin necesidad de bloquear hilos.

Características clave de CompletableFuture

  1. Encadenamiento de tareas: Permite encadenar tareas usando métodos como thenApply, thenAccept y thenRun.
  2. Ejecución asincrónica: Usa métodos como supplyAsync y runAsync para ejecutar tareas en pools de hilos.
  3. Manejo de errores: Proporciona métodos como exceptionally y handle para gestionar excepciones.

Principales métodos de CompletableFuture

  • supplyAsync(Supplier<T>): Ejecuta una tarea que devuelve un resultado.
  • runAsync(Runnable): Ejecuta una tarea que no devuelve resultado.
  • thenApply(Function<T, R>): Transforma el resultado de una tarea.
  • thenAccept(Consumer<T>): Consume el resultado de una tarea.
  • exceptionally(Function<Throwable, T>): Maneja excepciones.

Ejemplo de uso de CompletableFuture

import java.util.concurrent.*;

public class EjemploCompletableFuture {
    public static void main(String[] args) {
        CompletableFuture<String> futuro = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000); // Simula trabajo
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Resultado de CompletableFuture";
        });

        futuro.thenApply(resultado -> {
            System.out.println("Procesando resultado: " + resultado);
            return resultado.toUpperCase();
        }).thenAccept(resultadoFinal -> {
            System.out.println("Resultado final: " + resultadoFinal);
        }).exceptionally(e -> {
            System.out.println("Error: " + e.getMessage());
            return null;
        });

        // Esperar la terminación de las tareas
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

4. Diferencias clave entre Future y CompletableFuture

Característica Future CompletableFuture
Bloqueo Bloquea con get() No bloquea, usa callbacks
Encadenamiento No soportado Soportado
Manejo de errores Manual Incorporado
Ejecución asincrónica Limitada Totalmente soportada
API rica No

5. Buenas Prácticas

  1. Usar CompletableFuture para tareas complejas: Es ideal para flujos de trabajo reactivos y asincrónicos.
  2. Liberar recursos: Siempre cierra los pools de hilos con shutdown().
  3. Manejar excepciones: Usa exceptionally o handle para evitar errores no controlados.
  4. Evitar bloqueos innecesarios: Prefiere el encadenamiento sobre el uso de get().

6. Conclusión

Callable, Future y CompletableFuture son herramientas esenciales para manejar tareas concurrentes en Java. Mientras que Callable y Future ofrecen una base para la concurrencia, CompletableFuture permite construir aplicaciones más avanzadas, eficientes y reactivas. Dominar estas clases es fundamental para desarrollar aplicaciones modernas y escalables.