¿Cuál es exactamente la diferencia entre el tamaño del grupo principal y el tamaño máximo del grupo cuando hablamos en términos de ThreadPoolExecutor
?
¿Se puede explicar con la ayuda de un ejemplo?
¿Cuál es exactamente la diferencia entre el tamaño del grupo principal y el tamaño máximo del grupo cuando hablamos en términos de ThreadPoolExecutor
?
¿Se puede explicar con la ayuda de un ejemplo?
Respuestas:
Toma este ejemplo. El tamaño del grupo de subprocesos inicial es 1, el tamaño del grupo principal es 5, el tamaño máximo del grupo es 10 y la cola es 100.
A medida que ingresen solicitudes, se crearán hasta 5 subprocesos y luego se agregarán tareas a la cola hasta que llegue a 100. Cuando la cola esté llena, se crearán nuevos subprocesos hasta
maxPoolSize
. Una vez que todos los hilos estén en uso y la cola esté llena, las tareas serán rechazadas. A medida que se reduce la cola, también lo hace el número de subprocesos activos.
allowCoreThreadTimeOut(boolean)
que permite eliminar los subprocesos centrales después de un tiempo de inactividad determinado. Establecer esto en verdadero y establecer core threads
= max threads
permite que el grupo de subprocesos escale entre 0 y max threads
.
SI ejecuta subprocesos> corePoolSize & <maxPoolSize , cree un nuevo subproceso si la cola de tareas total está llena y está llegando una nueva.
Formulario doc: (Si hay más de Documento de corePoolSize pero menos de maximumPoolSize ejecución, se un nuevo subproceso solo si la cola está llena).
Ahora, tome un ejemplo simple,
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(5, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));
Aquí, 5 es el corePoolSize , lo que significa que Jvm creará un nuevo hilo para una nueva tarea para las primeras 5 tareas. y otras tareas se agregarán a la cola hasta que la cola se llene (50 tareas).
10 es el maxPoolSize : JVM puede crear un máximo de 10 subprocesos. Significa que si ya hay 5 tareas / subprocesos en ejecución y la cola está llena con 50 tareas pendientes y si una nueva solicitud / tarea más está llegando a la cola, JVM creará nuevos subprocesos hasta 10 (subprocesos totales = 5 anteriores + 5 nuevos) ;
nuevo ArrayBlockingQueue (50) = es un tamaño total de cola; puede poner 50 tareas en cola.
una vez que los 10 subprocesos se estén ejecutando y si llega una nueva tarea, esa nueva tarea será rechazada.
Reglas para crear subprocesos internamente por SUN:
Si el número de subprocesos es menor que corePoolSize, cree un nuevo subproceso para ejecutar una nueva tarea.
Si la cantidad de subprocesos es igual (o mayor que) corePoolSize, coloque la tarea en la cola.
Si la cola está llena y el número de subprocesos es menor que maxPoolSize, cree un nuevo subproceso para ejecutar tareas.
Si la cola está llena y el número de subprocesos es mayor o igual que maxPoolSize, rechace la tarea.
Espero, esto es HelpFul ... y por favor corríjanme si me equivoco ...
Del doc :
Cuando se envía una nueva tarea en el método execute (java.lang.Runnable) y se están ejecutando menos de los subprocesos corePoolSize, se crea un nuevo subproceso para manejar la solicitud, incluso si otros subprocesos de trabajo están inactivos. Si hay más subprocesos que corePoolSize pero menos que maximumPoolSize en ejecución, se creará un nuevo subproceso solo si la cola está llena.
Además:
Al establecer corePoolSize y maximumPoolSize de la misma manera, crea un grupo de subprocesos de tamaño fijo. Al establecer maximumPoolSize en un valor esencialmente ilimitado como Integer.MAX_VALUE, permite que el grupo se adapte a un número arbitrario de tareas simultáneas. Por lo general, los tamaños de grupo máximo y principal se establecen solo en el momento de la construcción, pero también se pueden cambiar dinámicamente utilizando setCorePoolSize (int) y setMaximumPoolSize (int).
Si decide crear ThreadPoolExecutor
manualmente en lugar de utilizar la Executors
clase de fábrica, deberá crear y configurar uno utilizando uno de sus constructores. El constructor más extenso de esta clase es:
public ThreadPoolExecutor(
int corePoolSize,
int maxPoolSize,
long keepAlive,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
);
Como puede ver, puede configurar:
Limitar la cantidad de tareas simultáneas que se están ejecutando, dimensionar su grupo de subprocesos, representa un gran beneficio para su aplicación y su entorno de ejecución en términos de predictibilidad y estabilidad: una creación de subprocesos ilimitada eventualmente agotará los recursos en tiempo de ejecución y su aplicación podría experimentar como consecuencia , graves problemas de rendimiento que pueden conducir incluso a la inestabilidad de la aplicación.
Esa es una solución para solo una parte del problema: está limitando la cantidad de tareas que se ejecutan, pero no está limitando la cantidad de trabajos que se pueden enviar y poner en cola para su ejecución posterior. La aplicación experimentará escasez de recursos más adelante, pero eventualmente lo experimentará si la tasa de envío supera constantemente la tasa de ejecución.
La solución a este problema es: Proporcionar una cola de bloqueo al ejecutor para retener las tareas en espera. En el caso de que la cola se llene, la tarea enviada será "rechazada". losRejectedExecutionHandler
invoca cuando se rechaza el envío de una tarea, y es por eso que el verbo rechazado se citó en el elemento anterior. Puede implementar su propia política de rechazo o utilizar una de las políticas integradas proporcionadas por el marco.
Las políticas de rechazo predeterminadas hacen que el ejecutor lance un RejectedExecutionException
. Sin embargo, otras políticas integradas le permiten:
Reglas de un tamaño de grupo ThreadPoolExecutor
Las reglas para el tamaño de un ThreadPoolExecutor's
piscina generalmente se entienden mal, porque no funciona de la manera que usted cree que debería o de la manera que usted desea.
Toma este ejemplo. El tamaño del grupo de subprocesos inicial es 1, el tamaño del grupo principal es 5, el tamaño máximo del grupo es 10 y la cola es 100.
Sun's way: a medida que las solicitudes lleguen en subprocesos, se crearán hasta 5, luego se agregarán tareas a la cola hasta que llegue a 100. Cuando la cola esté llena, se crearán nuevos subprocesos hasta maxPoolSize
. Una vez que todos los hilos estén en uso y la cola esté llena, las tareas serán rechazadas. A medida que la cola se reduce, también lo hace el número de subprocesos activos.
Forma anticipada por el usuario: a medida que las solicitudes lleguen en subprocesos, se crearán hasta 10, luego las tareas se agregarán a la cola hasta que llegue a 100, momento en el que se rechazarán. El número de subprocesos cambiará de nombre al máximo hasta que la cola esté vacía. Cuando la cola está vacía, los hilos morirán hasta que queden corePoolSize
.
La diferencia es que los usuarios quieren comenzar a aumentar el tamaño del grupo antes y quieren que la cola sea más pequeña, mientras que el método Sun quiere mantener el tamaño del grupo pequeño y solo aumentarlo una vez que la carga sea excesiva.
Aquí están las reglas de Sun para la creación de hilos en términos simples:
corePoolSize
, cree un nuevo subproceso para ejecutar una nueva tarea.corePoolSize
, coloque la tarea en la cola.maxPoolSize
, cree un nuevo subproceso para ejecutar tareas.maxPoolSize
, rechace la tarea. Lo más largo y corto es que los nuevos subprocesos solo se crean cuando la cola se llena, por lo que si está utilizando una cola ilimitada, la cantidad de subprocesos no excederá corePoolSize
.Para una explicación más completa, obténgalo de boca de los caballos: ThreadPoolExecutor
documentación API.
Hay una publicación en el foro realmente buena que le explica cómo ThreadPoolExecutor
funciona con ejemplos de código: http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0
Más información: http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450
Puede encontrar la definición de los términos corepoolsize y maxpoolsize en el javadoc. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
El enlace de arriba tiene la respuesta a su pregunta. Sin embargo, solo para dejarlo claro. La aplicación seguirá creando subprocesos hasta que llegue a corePoolSize. Creo que la idea aquí es que estos muchos subprocesos deberían ser suficientes para manejar la entrada de tareas. Si surge una nueva tarea después de que se crean los subprocesos corePoolSize, las tareas se pondrán en cola. Una vez que la cola está llena, el ejecutor comenzará a crear nuevos hilos. Es una especie de equilibrio. Lo que esencialmente significa es que la entrada de tareas es más que la capacidad de procesamiento. Por lo tanto, Executor comenzará a crear nuevos subprocesos nuevamente hasta que alcance el número máximo de subprocesos. Nuevamente, se crearán nuevos hilos si y solo si la cola está llena.
Buena explicación en este blog:
public class ThreadPoolExecutorExample {
public static void main (String[] args) {
createAndRunPoolForQueue(new ArrayBlockingQueue<Runnable>(3), "Bounded");
createAndRunPoolForQueue(new LinkedBlockingDeque<>(), "Unbounded");
createAndRunPoolForQueue(new SynchronousQueue<Runnable>(), "Direct hand-off");
}
private static void createAndRunPoolForQueue (BlockingQueue<Runnable> queue,
String msg) {
System.out.println("---- " + msg + " queue instance = " +
queue.getClass()+ " -------------");
ThreadPoolExecutor e = new ThreadPoolExecutor(2, 5, Long.MAX_VALUE,
TimeUnit.NANOSECONDS, queue);
for (int i = 0; i < 10; i++) {
try {
e.execute(new Task());
} catch (RejectedExecutionException ex) {
System.out.println("Task rejected = " + (i + 1));
}
printStatus(i + 1, e);
}
e.shutdownNow();
System.out.println("--------------------\n");
}
private static void printStatus (int taskSubmitted, ThreadPoolExecutor e) {
StringBuilder s = new StringBuilder();
s.append("poolSize = ")
.append(e.getPoolSize())
.append(", corePoolSize = ")
.append(e.getCorePoolSize())
.append(", queueSize = ")
.append(e.getQueue()
.size())
.append(", queueRemainingCapacity = ")
.append(e.getQueue()
.remainingCapacity())
.append(", maximumPoolSize = ")
.append(e.getMaximumPoolSize())
.append(", totalTasksSubmitted = ")
.append(taskSubmitted);
System.out.println(s.toString());
}
private static class Task implements Runnable {
@Override
public void run () {
while (true) {
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
break;
}
}
}
}
}
Salida:
---- Bounded queue instance = class java.util.concurrent.ArrayBlockingQueue -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 3, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 3, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 2, corePoolSize = 2, queueSize = 1, queueRemainingCapacity = 2, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 2, corePoolSize = 2, queueSize = 2, queueCapacity = 1, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 2, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 5
poolSize = 3, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 6
poolSize = 4, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 7
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 8
Task rejected = 9
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 9
Task rejected = 10
poolSize = 5, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
---- Unbounded queue instance = class java.util.concurrent.LinkedBlockingDeque -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 2147483647, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 2147483647, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 2, corePoolSize = 2, queueSize = 1, queueRemainingCapacity = 2147483646, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 2, corePoolSize = 2, queueSize = 2, queueRemainingCapacity = 2147483645, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 2, corePoolSize = 2, queueSize = 3, queueRemainingCapacity = 2147483644, maximumPoolSize = 5, totalTasksSubmitted = 5
poolSize = 2, corePoolSize = 2, queueSize = 4, queueRemainingCapacity = 2147483643, maximumPoolSize = 5, totalTasksSubmitted = 6
poolSize = 2, corePoolSize = 2, queueSize = 5, queueRemainingCapacity = 2147483642, maximumPoolSize = 5, totalTasksSubmitted = 7
poolSize = 2, corePoolSize = 2, queueSize = 6, queueRemainingCapacity = 2147483641, maximumPoolSize = 5, totalTasksSubmitted = 8
poolSize = 2, corePoolSize = 2, queueSize = 7, queueRemainingCapacity = 2147483640, maximumPoolSize = 5, totalTasksSubmitted = 9
poolSize = 2, corePoolSize = 2, queueSize = 8, queueRemainingCapacity = 2147483639, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
---- Direct hand-off queue instance = class java.util.concurrent.SynchronousQueue -------------
poolSize = 1, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 1
poolSize = 2, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 2
poolSize = 3, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 3
poolSize = 4, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 4
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 5
Task rejected = 6
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 6
Task rejected = 7
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 7
Task rejected = 8
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 8
Task rejected = 9
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 9
Task rejected = 10
poolSize = 5, corePoolSize = 2, queueSize = 0, queueRemainingCapacity = 0, maximumPoolSize = 5, totalTasksSubmitted = 10
--------------------
Process finished with exit code 0
Del libro Conceptos básicos de la concurencia de Java :
CorePoolSize : ThreadPoolExecutor tiene un atributo corePoolSize que determina cuántos subprocesos comenzará hasta que los nuevos subprocesos solo se inicien cuando la cola esté llena
MaximumPoolSize : este atributo determina cuántos subprocesos se inician como máximo. Puede establecer esto en Integer. MAX_VALUE para no tener un límite superior
java.util.concurrent.ThreadPoolExecutor
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
Comprender el comportamiento interno de ThreadPoolExecutor
cuando se envía una nueva tarea me ayudó a comprender cómo corePoolSize
ymaximumPoolSize
diferenciar.
Dejar:
N
el número de procesos en la piscina, getPoolSize()
. Subprocesos activos + subprocesos inactivos.T
ser la cantidad de tareas enviadas al ejecutor / grupo.C
ser el tamaño del núcleo de la piscina, getCorePoolSize()
. Cuántos subprocesos se pueden crear como máximo por grupo para las tareas entrantes antes de que las nuevas tareas pasen a la cola .M
el tamaño máximo de la agrupación, getMaximumPoolSize()
. Cantidad máxima de subprocesos que puede asignar el grupo.Comportamientos de ThreadPoolExecutor
en Java cuando se envía una nueva tarea:
N <= C
, a los subprocesos inactivos no se les asigna la nueva tarea entrante, sino que se crea un nuevo subproceso.N > C
y si hay subprocesos inactivos, entonces se asigna una nueva tarea allí.N > C
y si NO hay subprocesos inactivos, las nuevas tareas se colocan en la cola. AQUÍ NO SE CREÓ UN HILO NUEVO.M
. Si M
se alcanza, rechazamos las tareas. ¡Lo que es importante no aquí es que no creamos nuevos hilos hasta que la cola esté llena!Fuentes:
corePoolSize = 0
y maximumPoolSize = 10
con una capacidad de cola de 50
.Esto dará como resultado un único hilo activo en el grupo hasta que la cola tenga 50 elementos.
executor.execute(task #1):
before task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
after task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
[task #1 immediately queued and kicked in b/c the very first thread is created when `workerCountOf(recheck) == 0`]
execute(task #2):
before task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
after task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@c52dafe[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
[task #2 not starting before #1 is done]
... executed a few tasks...
execute(task #19)
before task #19 submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 17, completed tasks = 0]
after task #19 submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 18, completed tasks = 0]
...
execute(task #51)
before task submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 1, active threads = 1, queued tasks = 50, completed tasks = 0]
after task submitted to executor: java.util.concurrent.ThreadPoolExecutor@735afe38[Running, pool size = 2, active threads = 2, queued tasks = 50, completed tasks = 0]
Queue is full.
A new thread was created as the queue was full.
corePoolSize = 10
y maximumPoolSize = 10
con una capacidad de cola de 50
.Esto dará como resultado 10 subprocesos activos en el grupo. Cuando la cola tiene 50 elementos, las tareas se rechazarán.
execute(task #1)
before task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
after task #1 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
execute(task #2)
before task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
after task #2 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
execute(task #3)
before task #3 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
after task #3 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
... executed a few tasks...
execute(task #11)
before task #11 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = 0]
after task #11 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 1, completed tasks = 0]
... executed a few tasks...
execute(task #51)
before task #51 submitted to executor: java.util.concurrent.ThreadPoolExecutor@32d9e072[Running, pool size = 10, active threads = 10, queued tasks = 50, completed tasks = 0]
Task was rejected as we have reached `maximumPoolSize`.