Pool De Hilos Executor Executorservice Y Schedule
Pool de Hilos: Executor, ExecutorService y ScheduledExecutorService
En aplicaciones concurrentes, el uso eficiente de los hilos es crucial para maximizar el rendimiento y minimizar el tiempo de espera. Los pools de hilos son una solución eficiente para manejar tareas concurrentes reutilizando un conjunto fijo de hilos. Java proporciona varias interfaces y clases para trabajar con pools de hilos, siendo las principales Executor, ExecutorService y ScheduledExecutorService. Este documento te guiará a través de estos conceptos y cómo utilizarlos de manera efectiva.
1. Introducción a Executor
La interfaz Executor es el punto de entrada al marco de trabajo de ejecución de tareas en Java. Su método principal es:
void execute(Runnable command): Ejecuta una tarea proporcionada en un hilo del pool.
Ejemplo básico de Executor
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class EjemploExecutor {
public static void main(String[] args) {
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(() -> System.out.println("Tarea ejecutada en un pool de hilos"));
}
}
En este ejemplo, se crea un Executor de un solo hilo para ejecutar una tarea. Aunque Executor es simple, carece de funcionalidades avanzadas, lo que lleva al uso de ExecutorService.
2. Introducción a ExecutorService
ExecutorService extiende Executor y proporciona un control más avanzado sobre los pools de hilos, como:
- Gestionar la terminación de tareas.
- Devolver resultados mediante
Future. - Programar tareas con tiempos de espera.
Principales métodos de ExecutorService
submit(Runnable task): Envía una tarea para su ejecución y devuelve unFuture.submit(Callable<T> task): Similar aRunnable, pero devuelve un resultado.shutdown(): Inicia el cierre ordenado del pool de hilos.shutdownNow(): Intenta detener inmediatamente todas las tareas.awaitTermination(long timeout, TimeUnit unit): Espera a que el pool termine dentro de un tiempo especificado.
Ejemplo de ExecutorService
import java.util.concurrent.*;
public class EjemploExecutorService {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Runnable tarea1 = () -> System.out.println("Tarea 1 ejecutada por: " + Thread.currentThread().getName());
Runnable tarea2 = () -> System.out.println("Tarea 2 ejecutada por: " + Thread.currentThread().getName());
executorService.submit(tarea1);
executorService.submit(tarea2);
executorService.shutdown();
}
}
En este ejemplo, un ExecutorService con un pool de 3 hilos ejecuta dos tareas concurrentemente.
3. Introducción a ScheduledExecutorService
ScheduledExecutorService extiende ExecutorService y permite programar tareas para que se ejecuten después de un retraso o de manera periódica.
Principales métodos de ScheduledExecutorService
schedule(Runnable command, long delay, TimeUnit unit): Programa una tarea para que se ejecute después de un retraso.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit): Programa tareas para que se ejecuten a intervalos fijos.scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit): Programa tareas para que se ejecuten con un retraso fijo entre la terminación de una y el inicio de la siguiente.
Ejemplo de ScheduledExecutorService
import java.util.concurrent.*;
public class EjemploScheduledExecutorService {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
Runnable tarea = () -> System.out.println("Tarea programada ejecutada en: " + Thread.currentThread().getName());
scheduler.schedule(tarea, 5, TimeUnit.SECONDS); // Ejecuta una vez después de 5 segundos
scheduler.scheduleAtFixedRate(tarea, 1, 3, TimeUnit.SECONDS); // Cada 3 segundos, después de 1 segundo inicial
scheduler.scheduleWithFixedDelay(tarea, 2, 4, TimeUnit.SECONDS); // Retraso fijo de 4 segundos entre ejecuciones
}
}
En este ejemplo, se muestra cómo programar tareas con diferentes patrones usando ScheduledExecutorService.
4. Elección del Pool de Hilos
Java proporciona varias implementaciones de pools de hilos en Executors:
newFixedThreadPool(int nThreads): Crea un pool con un número fijo de hilos.newCachedThreadPool(): Crea un pool con un número dinámico de hilos. Ideal para tareas cortas y frecuentes.newSingleThreadExecutor(): Crea un pool con un único hilo.newScheduledThreadPool(int corePoolSize): Crea un pool para tareas programadas.
5. Buenas Prácticas
- Siempre cerrar el pool de hilos: Usa
shutdown()oshutdownNow()para liberar recursos. - Evitar crear hilos manualmente: Usa pools de hilos para evitar problemas de rendimiento.
- Elegir el pool adecuado: Adapta el tipo de pool al caso de uso.
- Manejar excepciones: Implementa un
ThreadFactorypersonalizado o maneja excepciones dentro de las tareas. - No bloquear indefinidamente: Usa tiempos de espera en
awaitTerminationyFuture.
6. Conclusión
Los pools de hilos son una herramienta poderosa para manejar tareas concurrentes de manera eficiente. Executor, ExecutorService y ScheduledExecutorService ofrecen diferentes niveles de control y flexibilidad. Dominar estas herramientas es esencial para construir aplicaciones escalables y de alto rendimiento.