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 un Future.
  • submit(Callable<T> task): Similar a Runnable, 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:

  1. newFixedThreadPool(int nThreads): Crea un pool con un número fijo de hilos.
  2. newCachedThreadPool(): Crea un pool con un número dinámico de hilos. Ideal para tareas cortas y frecuentes.
  3. newSingleThreadExecutor(): Crea un pool con un único hilo.
  4. newScheduledThreadPool(int corePoolSize): Crea un pool para tareas programadas.

5. Buenas Prácticas

  1. Siempre cerrar el pool de hilos: Usa shutdown() o shutdownNow() para liberar recursos.
  2. Evitar crear hilos manualmente: Usa pools de hilos para evitar problemas de rendimiento.
  3. Elegir el pool adecuado: Adapta el tipo de pool al caso de uso.
  4. Manejar excepciones: Implementa un ThreadFactory personalizado o maneja excepciones dentro de las tareas.
  5. No bloquear indefinidamente: Usa tiempos de espera en awaitTermination y Future.

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.