Al desarrollar una aplicación de subprocesos múltiples, generalmente debemos ocuparnos de organizar el trabajo de los subprocesos. Cuanto más grande sea nuestra aplicación y más subprocesos necesitemos para tareas multiproceso, más Ejecutable objetos que creamos.

Cabe señalar aquí que crear un hilo en Java es una operación bastante costosa. Si creamos una nueva instancia del subproceso cada vez que realizamos una operación, tendremos grandes problemas con el rendimiento y, como resultado, con la salud de la aplicación.

Un grupo de subprocesos y ThreadPoolExecutor vienen en nuestra ayuda aquí.

Un grupo de subprocesos es un conjunto de subprocesos preinicializados. Su tamaño puede ser fijo o variable.

Si hay más tareas que subprocesos, las tareas esperan en una cola de tareas. El enésimo subproceso en el grupo toma una tarea de la cola y, una vez finalizada, el subproceso toma una nueva tarea de la cola. Una vez que se ejecutan todas las tareas en la cola, los subprocesos permanecen activos y esperan nuevas tareas. Cuando aparecen nuevas tareas, los subprocesos comienzan a ejecutarlas también.

ThreadPoolExecutor

A partir de Java 5, el marco Executor obtuvo una solución de subprocesos múltiples. En general, tiene muchos componentes y su propósito es ayudarnos a administrar de manera eficiente las colas y los grupos de subprocesos.

Las interfaces principales son Executor y ExecutorService .

Executor es una interfaz con un solo método de ejecución nulo (ejecutable ejecutable).

Al pasar una tarea a una implementación de este método, sepa que se ejecutará de forma asíncrona en el futuro.

ExecutorService : una interfaz que amplía la interfaz Executor y agrega capacidades para ejecutar tareas. También tiene métodos para interrumpir una tarea en ejecución y terminar el grupo de subprocesos.

ThreadPoolExecutor implementa las interfaces Executor y ExecutorService y separa la creación de tareas de la ejecución de tareas. Necesitamos implementar objetos Runnable y enviarlos a un ejecutor. El ThreadPoolExecutor es entonces responsable de ejecutar las tareas y crear y trabajar con subprocesos.

Después de que se envía una tarea para su ejecución, se usa un subproceso existente en el grupo. Esto mejora el rendimiento. Resuelve el problema de desperdiciar recursos en la creación e inicialización de un nuevo subproceso, y luego nuevamente en la recolección de elementos no utilizados una vez que hayamos terminado con el subproceso.

ThreadPoolExecutor tiene 4 constructores:


ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit, 
BlockingQueue<Runnable> workQueue)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, 
RejectedExecutionHandler handler)
    

El constructor ThreadPoolExecutor tiene los siguientes parámetros:

corePoolSize Este parámetro indica cuántos subprocesos estarán listos (iniciados) cuando se inicie el servicio ejecutor.
tamaño máximo de grupo El número máximo de subprocesos que puede crear un servicio ejecutor.
mantener el tiempo vivo El tiempo que seguirá viviendo un subproceso liberado antes de ser destruido si el número de subprocesos es mayor que corePoolSize. Las unidades de tiempo se especifican en el siguiente parámetro.
unidad Unidades de tiempo (horas, minutos, segundos, milisegundos, etc.).
cola de trabajo Implementación de una cola de tareas.
manipulador Controlador de tareas que no se pueden completar.
fábrica de hilos Un objeto que crea nuevos hilos a pedido. El uso de fábricas de subprocesos hace que las llamadas a un nuevo subproceso sean independientes del hardware, lo que permite que las aplicaciones utilicen subclases de subprocesos especiales, prioridades, etc.

Creando un ThreadPoolExecutor

La clase de utilidad Executors puede simplificar la creación de un ThreadPoolExecutor . Los métodos de esta clase de utilidad nos ayudan a preparar un ThreadPoolExecutor objeto.

newFixedThreadPool : crea un grupo de subprocesos que reutiliza una cantidad fija de subprocesos para ejecutar cualquier cantidad de tareas.

ExecutorService executor = Executors.newFixedThreadPool(10);
                    
newWorkStealingPool : crea un grupo de subprocesos donde el número de subprocesos es igual al número de núcleos de procesador disponibles para la JVM. El nivel de concurrencia predeterminado es uno. Esto significa que se crearán tantos subprocesos en el grupo como núcleos de CPU haya disponibles para la JVM. Si el nivel de simultaneidad es 4, se utiliza el valor pasado en lugar del número de núcleos.

ExecutorService executor = Executors.newWorkStealingPool(4);
                    
newSingleThreadExecutor : crea un grupo con un único subproceso para ejecutar todas las tareas.

ExecutorService executor = Executors.newSingleThreadExecutor();
                    
newCachedThreadPool : crea un grupo de subprocesos que crea nuevos subprocesos según sea necesario, pero reutiliza los subprocesos creados anteriormente cuando están disponibles.

ExecutorService executor = Executors.newCachedThreadPool();
                    
newScheduledThreadPool : crea un grupo de subprocesos que puede programar comandos para que se ejecuten después de un retraso determinado o periódicamente.

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
                    

Consideraremos cada tipo de piscina en las siguientes lecciones.