3 Creación de un ThreadPoolExecutor¶
Creación de un ThreadPoolExecutor¶
El primer paso para trabajar con el Executor framework es crear un objeto ejecutor. Todas la clases que representan ejecutores extienden de la clase abstracta AbstractExecutorService
, que a su vez implementa la interfaz ExecutorService
, y por ello Executor
.
Aunque existen distintas clases que definen tipos de ejecutores, comenzaremos con la clase ThreadPoolExecutor
, que extiende AbstractExecutorService
.
Internamente un ThreadPoolExecutor
se configura en base a los siguientes parámetros:
- corePoolSize: Número de hilos que deben mantenerse en el pool incluso aunque estén sin ejecutar ninguna tarea.
- maximumPoolSize: Número máximo de hilos que puede haber en el threadpool.
- keepAliveTime: Indica el tiempo máximo que un hilo esperará a que le entreguen una nueva tarea antes de finalizar, siempre y cuando el número actual de hilos del threadpool sea mayor que corePoolSize. Esto permite que los hilos se vayan finalizando cuando ya no son necesarios y que el tamaño del threadpool vaya decreciendo.
- keepAliveTimeUnit: Unidad de tiempo correspondiente al valor de keepAliveTime.
- workQueue: Cola de trabajo
BlockingQueue<Runnable>
en la que se almacenan las tareas antes de ser ejecutadas. - threadFactory: Factoría de hilos que usa el ejecutor para crear un hilo cuando es necesario. Corresponderá a un objeto de una clase que implemente la interfaz
ThreadFactory
. Si no se establece se crea una por defecto, correspondiente al objeto retornado por el método estático factoríaExecutors.defaultThreadFactory()
. - rejectedExecutionHandler: Gestor de tareas que hayan sido rechazadas por el ejecutor. Corresponderá a un objeto de una clase que implemente la interfaz
RejectedExecutionHandler
. Si no se establece uno, la excepciónRejectedException
no será gestionada por el ejecutor sino que simplemente será lanzada.
La clase ThreadPoolExecutor
posee distintos constructores sobrecargados para crear un threadpool executor configurando los parámetros anteriores. Por ejemplo, el más simple corresponde a:
También tendremos los métodos getter y setter correspondientes a dichos parámetros de configuración del ejecutor, como getCorePoolSize()
, setCorePoolSize()
, etc., excepto setQueue(blockingQueue)
, que no existe.
Dado que se tratan de muchos parámetros y que hay que entender bien el funcionamiento interno del ejecutor para poder configurarlos, la clase Executors
nos facilita la construcción de ejecutores proporcionándonos una serie de métodos estáticos factoría especializados en la creación de los tipos de threadpool executors mas habituales, que llaman al constructor de la clase ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, keepAliveTimeUnit, workQueue)
con los argumentos adecuados.
Así tenemos los métodos factoría Executors.newCachedThreadPool()
, Executors.newFixedThreadPool(numberOfThreads)
, Executors.newSingleThreadExecutor()
, etc. Estudiaremos estos métodos dentro de poco.
La clase ThreadPoolExecutor
proporciona algunos métodos informativos que nos permiten obtener información sobre las tareas que ejecuta el ejecutor:
getPoolSize()
: Retorna el número actual de hilos en el threadpool gestionado por el ejecutor.getActiveCount()
: Retorna el número de hilos que están actualmente ejecutando tareas en el ejecutor.getCompletedTaskCount()
: Retorna el número de tareas completadas por el ejecutor.getTaskCount()
: Retorna el número de tareas que han sido enviadas al ejecutor.getLargestPoolSize()
: Retorna el número máximo de hilos que han sido utilizados a la vez por el ejecutor en un momento dado. Es decir, el tamaño máximo que ha alcanzado el threadpool.