Tamaño del grupo principal frente al tamaño máximo del grupo en ThreadPoolExecutor

Respuestas:

130

De esta publicación de blog :

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.

usuario2568266
fuente
¿Es esto correcto? Pensé que se crearían nuevos subprocesos hasta que alcance maxPoolSize. Luego, los nuevos hilos se colocarán en la cola. Por favor corríjame si me equivoco ..
Glide
4
Si eso es correcto. Los subprocesos solo se agregarán más allá de corePoolSize si hay tareas en la cola. Estos subprocesos adicionales "desaparecerán" después de que la cola llegue a cero.
Lucas
3
Existe un método interesante 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 threadspermite que el grupo de subprocesos escale entre 0 y max threads.
Jaroslaw Pawlak
4
Lo acaba de copiar de aquí bigsoft.co.uk/blog/index.php/2009/11/27/…
Kumar Manish
1
¿Qué pasa con las tareas rechazadas?
Cera
54

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:

  1. Si el número de subprocesos es menor que corePoolSize, cree un nuevo subproceso para ejecutar una nueva tarea.

  2. Si la cantidad de subprocesos es igual (o mayor que) corePoolSize, coloque la tarea en la cola.

  3. Si la cola está llena y el número de subprocesos es menor que maxPoolSize, cree un nuevo subproceso para ejecutar tareas.

  4. 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 ...

Darshan Dalwadi
fuente
21

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).

Brian Agnew
fuente
1) Cuando se envía una nueva tarea en el método ejecutar (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. ¿Por qué es necesario crear un nuevo hilo para manejar la solicitud si hay hilos inactivos?
user2568266
1
2) 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. No entiendo la diferencia entre corePoolSize y maximumPoolSize aquí. En segundo lugar, ¿cómo puede una cola estar llena cuando los subprocesos son menores que maximumPoolSize? La cola solo puede estar llena si los subprocesos son iguales a maximumPoolSize. ¿No es así?
user2568266
9

Si decide crear ThreadPoolExecutormanualmente en lugar de utilizar la Executorsclase 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:

  • El tamaño del grupo de núcleos (el tamaño con el que el grupo de subprocesos intentará mantener).
  • El tamaño máximo de la piscina.
  • El tiempo de mantener vivo, que es un tiempo después del cual un hilo inactivo es elegible para ser eliminado.
  • La cola de trabajo para mantener las tareas pendientes de ejecución.
  • La política que se aplicará cuando se rechace el envío de una tarea.

Limitar el número de tareas en cola

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:

  • Descarte un trabajo en silencio.
  • Descarte el trabajo más antiguo e intente volver a enviar el último.
  • Ejecute la tarea rechazada en el hilo de la persona que llama.
Prashant Gautam
fuente
5

Fuente

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:

  1. Si el número de subprocesos es menor que el corePoolSize, cree un nuevo subproceso para ejecutar una nueva tarea.
  2. Si el número de subprocesos es igual (o mayor que) corePoolSize, coloque la tarea en la cola.
  3. Si la cola está llena y la cantidad de subprocesos es menor que maxPoolSize, cree un nuevo subproceso para ejecutar tareas.
  4. Si la cola está llena y el número de subprocesos es mayor o igual que 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: ThreadPoolExecutordocumentación API.

Hay una publicación en el foro realmente buena que le explica cómo ThreadPoolExecutorfunciona 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

Premraj
fuente
3

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.

Braj
fuente
3

Buena explicación en este blog:

Ilustración

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
rxt66
fuente
1

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

Ramesh Papaganti
fuente
0

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);
    }
宏杰 李
fuente
0

Comprender el comportamiento interno de ThreadPoolExecutorcuando se envía una nueva tarea me ayudó a comprender cómo corePoolSizeymaximumPoolSize diferenciar.

Dejar:

  • Nel número de procesos en la piscina, getPoolSize(). Subprocesos activos + subprocesos inactivos.
  • T ser la cantidad de tareas enviadas al ejecutor / grupo.
  • Cser 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 .
  • Mel tamaño máximo de la agrupación, getMaximumPoolSize(). Cantidad máxima de subprocesos que puede asignar el grupo.

Comportamientos de ThreadPoolExecutoren Java cuando se envía una nueva tarea:

  • por N <= C , a los subprocesos inactivos no se les asigna la nueva tarea entrante, sino que se crea un nuevo subproceso.
  • Para N > Cy si hay subprocesos inactivos, entonces se asigna una nueva tarea allí.
  • Para N > Cy si NO hay subprocesos inactivos, las nuevas tareas se colocan en la cola. AQUÍ NO SE CREÓ UN HILO NUEVO.
  • Cuando la cola está llena , creamos nuevos hilos hasta M. Si Mse alcanza, rechazamos las tareas. ¡Lo que es importante no aquí es que no creamos nuevos hilos hasta que la cola esté llena!

Fuentes:

Ejemplos

Ejemplo con corePoolSize = 0y maximumPoolSize = 10con 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.

Ejemplo con corePoolSize = 10y maximumPoolSize = 10con 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`. 
Andrés
fuente