1. Ejecución Asíncrona con Retorno (supplyAsync)
Utiliza una interfaz Supplier cuando necesitas que la tarea asíncrona devuelva un valor tras su ejecución.
CompletableFuture<Integer> asyncCalculation = CompletableFuture.supplyAsync(() -> {
executeBusinessLogic("Cálculo de métricas");
return 42;
});
2. Ejecución Asíncrona sin Retorno (runAsync)
Emplea una interfaz Runnable para tareas en segundo plano donde el resultado final no es necesario.
CompletableFuture<Void> backgroundTask = CompletableFuture.runAsync(() -> {
executeBusinessLogic("Sincronización de caché");
});
3. Fusión de Resultados (thenCombine)
Permite ejecutar dos tareas en paralelo y combinar sus salidas una vez que ambas hayan finalizado.
CompletableFuture<String> fetchUserData = CompletableFuture.supplyAsync(() -> {
processData("Perfil de usuario");
return "Usuario_X";
});
CompletableFuture<String> fetchPreferences = CompletableFuture.supplyAsync(() -> {
processData("Preferencias");
return "Tema_Oscuro";
});
CompletableFuture<String> aggregatedData = fetchUserData.thenCombine(fetchPreferences, (user, prefs) -> {
return "Datos consolidados: " + user + " con " + prefs;
});
4. Encadenamiento de Operaciones
thenApply: Transforma el resultado de unCompletableFutureen un nuevo tipo.thenAccept: Procesa el resultado sin generar un nuevoCompletableFuture.thenRun: Ejecuta una acción secundaria ignorando el resultado de la etapa anterior.
CompletableFuture<String> initialTask = CompletableFuture.supplyAsync(() -> {
processData("Extracción de datos");
return "RawData";
});
initialTask.thenApply(raw -> {
return "Procesado: " + raw;
}).thenAccept(formatted -> {
System.out.println("Contenido final: " + formatted);
}).thenRun(() -> {
System.out.println("Pipeline de procesamiento finalizado");
});
5. Manejo Básico de Errores (exceptionally)
Proporciona un valor de respaldo o recuperación cuando la tarea asíncrona falla.
CompletableFuture<String> resilientTask = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new IllegalStateException("Fallo en la conexión");
}
return "Datos válidos";
}).exceptionally(error -> {
return "Recuperado: " + error.getMessage();
});
resilientTask.thenAccept(System.out::println);
6. Gestión Unificada de Resultados y Excepciones (handle)
Permite manejar tanto el éxito como el fallo en una única etapa, evaluando si la excepción es nula.
CompletableFuture<String> controlledTask = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new IllegalStateException("Timeout excedido");
}
return "Operación exitosa";
}).handle((outcome, error) -> {
if (error != null) {
return "Error capturado: " + error.getMessage();
}
return "Éxito: " + outcome;
});
controlledTask.thenAccept(System.out::println);
7. Aplanamiento de Tareas Dependientes (thenCompose)
Ideal para encadenar tareas donde el resultado de la primera es el argumento de entrada de la segunda, evitando anidamiento de CompletableFuture.
CompletableFuture<String> primaryOperation = CompletableFuture.supplyAsync(() -> {
processData("Obtener ID de recurso");
return "RES-992";
});
CompletableFuture<String> secondaryOperation = primaryOperation.thenCompose(resourceId -> {
return CompletableFuture.supplyAsync(() -> {
processData("Consultar detalles de " + resourceId);
return "Detalles del recurso " + resourceId;
});
});
secondaryOperation.thenAccept(System.out::println);
8. Sincronización de Múltiples Tareas (allOf)
Espera a que un conjunto de futuros se completen antes de continuar con la lógica principal.
CompletableFuture<String> batchJobA = CompletableFuture.supplyAsync(() -> {
processData("Lote A");
return "Resultado A";
});
CompletableFuture<String> batchJobB = CompletableFuture.supplyAsync(() -> {
processData("Lote B");
return "Resultado B";
});
CompletableFuture<Void> allJobs = CompletableFuture.allOf(batchJobA, batchJobB);
allJobs.thenRun(() -> {
System.out.println("Todos los lotes han terminado su ejecución");
});
Optimización de Recursos con Executor Personalizado
Por defecto, CompletableFuture utiliza el ForkJoinPool.commonPool(). En aplicaciones de producción, es recomendable inyectar un ExecutorService personalizado para aislar recursos y evitar bloqueos.
ExecutorService dedicatedPool = Executors.newFixedThreadPool(15);
CompletableFuture<String> isolatedTask = CompletableFuture.supplyAsync(() -> {
return "Ejecutado en pool personalizado";
}, dedicatedPool);
Aislamiento de Fallos en Operaciones Combinadas
Al fusionar múltiples futuros, aplica manejo de excepciones a cada uno de forma individual. Esto previene que un fallo en una rama propague la excepción y cancele las demás ramas del flujo.
CompletableFuture<String> streamAlpha = CompletableFuture.supplyAsync(() -> "Flujo Alfa").exceptionally(ex -> "Fallback Alfa");
CompletableFuture<String> streamBeta = CompletableFuture.supplyAsync(() -> "Flujo Beta").exceptionally(ex -> "Fallback Beta");
CompletableFuture<String> mergedStreams = streamAlpha.thenCombine(streamBeta, (alpha, beta) -> alpha + " | " + beta);