java multithreading threadpool executorservice java.util.concurrent

java - FixedThreadPool vs CachedThreadPool: el menor de dos males



multithreading executorservice (3)

Entonces tengo un programa que engendra hilos (~ 5-150) que realizan un montón de tareas. Originalmente, utilicé FixedThreadPool porque esta pregunta similar sugería que eran más adecuados para tareas de mayor duración y, dado mi conocimiento muy limitado sobre el multihilo, consideré que la vida media de los hilos (varios minutos) era " larga duración ".

Sin embargo, recientemente agregué la capacidad de engendrar hilos adicionales y hacerlo me lleva por encima del límite de hilo que establecí. En este caso, ¿sería mejor adivinar y aumentar el número de hilos que puedo permitir o cambiar a un CachedThreadPool para no tener desperdicio de hilos?

Probándolos a ambos de forma preliminar, no parece haber una diferencia, así que me inclino a ir con el CachedThreadPool solo para evitar el desperdicio. Sin embargo, ¿la vida útil de los hilos significa que debería FixedThreadPool un FixedThreadPool y solo tratar con los hilos no utilizados? Esta pregunta hace que parezca que esos hilos adicionales no se desperdician, pero agradecería la aclaración.


Entonces tengo un programa que engendra hilos (~ 5-150) que realizan un montón de tareas.

¿Seguro que entiendes cómo los hilos son procesados ​​por tu sistema operativo y el hardware de tu elección? ¿Cómo mapea Java los hilos a los hilos del sistema operativo, cómo mapea los hilos a los hilos de la CPU, etc.? Lo estoy preguntando porque crear 150 hilos dentro de ONE JRE solo tiene sentido si tienes núcleos / hilos de CPU masivos debajo, lo cual no es el caso. Dependiendo del sistema operativo y la RAM en uso, la creación de más de n subprocesos incluso puede dar lugar a que su JRE se termine debido a errores OOM. Entonces, realmente debería distinguir entre los hilos y el trabajo que hacer por esos hilos, cuántos trabajos puede incluso procesar, etc.

Y ese es el problema con CachedThreadPool: no tiene sentido poner en cola el trabajo de larga ejecución en subprocesos que en realidad no se pueden ejecutar porque solo tiene 2 núcleos de CPU capaces de procesar esos subprocesos. Si termina con 150 subprocesos programados, puede crear una sobrecarga innecesaria para que los planificadores utilizados en Java y el sistema operativo los procesen simultáneamente. Esto es simplemente imposible si solo tiene 2 núcleos de CPU, a menos que sus hilos estén esperando E / S o todo el tiempo. Pero incluso en ese caso, muchos hilos crearían muchas E / S ...

Y ese problema no ocurre con FixedThreadPool, creado con, por ejemplo, 2 + n subprocesos, donde n es razonablemente bajo, por supuesto, porque con ese hardware y SO se utilizan recursos con mucha menos sobrecarga para gestionar subprocesos que no se pueden ejecutar de todos modos.


Tanto FixedThreadPool como CachedThreadPool son males en aplicaciones con mucha carga.

CachedThreadPool es más peligroso que FixedThreadPool

Si su aplicación está muy cargada y exige baja latencia, es mejor deshacerse de ambas opciones debido a los inconvenientes a continuación

  1. Naturaleza ilimitada de la cola de tareas: puede causar falta de memoria o alta latencia
  2. Los subprocesos de ejecución larga harán que CachedThreadPool se salga de control en la creación de subprocesos

Como sabes que ambos son males, el mal menor no hace el bien. Prefiera ThreadPoolExecutor , que proporciona control granular en muchos parámetros.

  1. Establezca la cola de tareas como cola limitada para tener un mejor control
  2. Tiene RejectionHandler derecho: tu propio RejectionHandler o tu controlador predeterminado proporcionado por JDK
  3. Si tiene algo que hacer antes / después de la finalización de la tarea, anule beforeExecute(Thread, Runnable) y afterExecute(Runnable, Throwable)
  4. Anular ThreadFactory si se requiere la personalización del hilo
  5. Controle el tamaño del grupo de subprocesos dinámicamente en el tiempo de ejecución (pregunta SE relacionada: Dynamic Thread Pool )

Un CachedThreadPool es exactamente lo que debe usar para su situación, ya que no hay consecuencias negativas en el uso de uno para los subprocesos de larga ejecución. El comentario en el documento de Java sobre CachedThreadPools es adecuado para tareas cortas, simplemente sugiere que son particularmente apropiadas para tales casos, no es que no puedan o no se utilicen para tareas que implican tareas de ejecución prolongada.

Para más detalles, Executors.newCachedThreadPool y Executors.newFixedThreadPool están respaldados por la misma implementación del grupo de subprocesos (al menos en el JDK abierto) solo con diferentes parámetros. Las diferencias son solo el mínimo, máximo, el tiempo de eliminación de subprocesos y el tipo de cola.

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }

Un FixedThreadPool tiene sus ventajas cuando realmente quiere trabajar con un número fijo de subprocesos, ya que puede enviar cualquier cantidad de tareas al servicio del ejecutor sabiendo que el número de subprocesos se mantendrá en el nivel que especificó. Si desea aumentar explícitamente el número de subprocesos, esta no es la opción adecuada.

Sin embargo, esto significa que el único problema que puede tener con CachedThreadPool es en cuanto a limitar el número de subprocesos que se ejecutan simultáneamente. CachedThreadPool no los limitará por usted, por lo que puede necesitar escribir su propio código para asegurarse de no ejecutar demasiados hilos. Esto realmente depende del diseño de su aplicación y de cómo se envían las tareas al servicio del ejecutor.