Tamanho do pool principal vs tamanho máximo do pool em ThreadPoolExecutor

105

Qual é exatamente a diferença entre o tamanho do pool principal e o tamanho máximo do pool quando falamos em termos de ThreadPoolExecutor?
Isso pode ser explicado com a ajuda de um exemplo?

user2568266
fonte

Respostas:

130

A partir deste post :

Veja este exemplo. O tamanho do conjunto de encadeamentos inicial é 1, o tamanho do conjunto principal é 5, o tamanho máximo do conjunto é 10 e a fila é 100.

Conforme as solicitações chegam, até 5 threads são criados e as tarefas são adicionadas à fila até chegar a 100. Quando a fila estiver cheia, novos threads serão criados até maxPoolSize. Quando todos os threads estiverem em uso e a fila cheia, as tarefas serão rejeitadas. Conforme a fila diminui, também diminui o número de threads ativos.

user2568266
fonte
Isso está correto? Pensei que novos threads seriam criados até atingir maxPoolSize. Então, todos os novos threads serão colocados na fila. Por favor, corrija-me se eu estiver errado ..
Glide
4
Sim isto está correcto. Threads só serão adicionados além de corePoolSize se houver tarefas na fila. Esses threads adicionais "morrerão" depois que a fila chegar a zero.
Lucas
3
Existe um método interessante allowCoreThreadTimeOut(boolean)que permite que os threads principais sejam eliminados após um determinado tempo inativo. Definir isso como verdadeiro e definir core threads= max threadspermite que o pool de threads seja dimensionado entre 0 e max threads.
Jaroslaw Pawlak
4
Você acabou de copiá-lo daqui bigsoft.co.uk/blog/index.php/2009/11/27/…
Kumar Manish
1
O que acontece com as tarefas rejeitadas?
Cera de
54

IF executando threads> corePoolSize & <maxPoolSize , crie uma nova Thread se a fila de tarefas Total estiver cheia e uma nova estiver chegando.

Documento de formulário: (se houver mais de corePoolSize, mas menos de threads maximumPoolSize em execução, um novo thread será criado apenas se a fila estiver cheia.)

Agora, pegue um exemplo simples,

ThreadPoolExecutor executorPool = new ThreadPoolExecutor(5, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));

Aqui, 5 é o corePoolSize - significa que Jvm criará um novo thread para uma nova tarefa para as 5 primeiras tarefas. e outras tarefas serão adicionadas à fila até que a fila fique cheia (50 tarefas).

10 é o maxPoolSize - JVM pode criar no máximo 10 encadeamentos. Significa que se já houver 5 tarefas / encadeamento em execução e a fila estiver cheia com 50 tarefas pendentes e se mais uma nova solicitação / tarefa estiver chegando na fila, a JVM criará um novo encadeamento até 10 (total de encadeamentos = 5 anteriores + 5 novos) ;

new ArrayBlockingQueue (50) = é o tamanho total da fila - ele pode enfileirar 50 tarefas nela.

quando todos os 10 threads estiverem em execução e se uma nova tarefa estiver chegando, essa nova tarefa será rejeitada.

Regras para criar Threads internamente pela SUN:

  1. Se o número de threads for menor que corePoolSize, crie um novo Thread para executar uma nova tarefa.

  2. Se o número de threads for igual (ou maior que) corePoolSize, coloque a tarefa na fila.

  3. Se a fila estiver cheia e o número de encadeamentos for menor que maxPoolSize, crie um novo encadeamento para executar tarefas.

  4. Se a fila estiver cheia e o número de threads for maior ou igual a maxPoolSize, rejeite a tarefa.

Espero, isso é útil .. e por favor me corrija se eu estiver errado ...

Darshan Dalwadi
fonte
21

Do doc :

Quando uma nova tarefa é enviada no método execute (java.lang.Runnable), e menos de threads corePoolSize estão em execução, um novo thread é criado para lidar com a solicitação, mesmo se outros threads de trabalho estiverem ociosos. Se houver mais de corePoolSize, mas menos de threads maximumPoolSize em execução, um novo thread será criado apenas se a fila estiver cheia.

Além disso:

Ao definir corePoolSize e maximumPoolSize iguais, você cria um pool de threads de tamanho fixo. Ao definir maximumPoolSize como um valor essencialmente ilimitado, como Integer.MAX_VALUE, você permite que o pool acomode um número arbitrário de tarefas simultâneas. Normalmente, os tamanhos de pool máximo e principal são definidos apenas na construção, mas também podem ser alterados dinamicamente usando setCorePoolSize (int) e setMaximumPoolSize (int).

Brian Agnew
fonte
1) Quando uma nova tarefa é enviada no método execute (java.lang.Runnable), e menos de threads corePoolSize estão em execução, um novo thread é criado para lidar com a solicitação, mesmo se outras threads de trabalho estiverem ociosas. Por que é necessário criar um novo encadeamento para lidar com a solicitação se houver encadeamentos ociosos?
user2568266
1
2) Se houver mais de corePoolSize, mas menos de maximumPoolSize threads em execução, um novo thread será criado apenas se a fila estiver cheia. Não entendo a diferença entre corePoolSize e maximumPoolSize aqui. Em segundo lugar, como uma fila pode estar cheia quando os threads são menores que maximumPoolSize? A fila só pode estar cheia se os threads forem iguais a maximumPoolSize. Não é?
user2568266
9

Se você decidir criar um ThreadPoolExecutormanualmente em vez de usar a Executorsclasse de fábrica, precisará criar e configurar um usando um de seus construtores. O construtor mais abrangente desta classe é:

public ThreadPoolExecutor(
    int corePoolSize,
    int maxPoolSize,
    long keepAlive,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    RejectedExecutionHandler handler
);

Como você pode ver, você pode configurar:

  • O tamanho do pool principal (o tamanho que o pool de threads tentará manter).
  • O tamanho máximo da piscina.
  • O tempo de keep alive, que é um tempo após o qual um encadeamento inativo é elegível para ser interrompido.
  • A fila de trabalho para reter tarefas que aguardam execução.
  • A política a ser aplicada quando o envio de uma tarefa é rejeitado.

Limitando o número de tarefas na fila

Limitar o número de tarefas simultâneas em execução, dimensionar seu pool de threads representa um grande benefício para seu aplicativo e seu ambiente de execução em termos de previsibilidade e estabilidade: uma criação de thread ilimitada acabará esgotando os recursos de tempo de execução e seu aplicativo pode experimentar como consequência , sérios problemas de desempenho que podem levar até mesmo à instabilidade do aplicativo.

Essa é uma solução para apenas uma parte do problema: você está limitando o número de tarefas sendo executadas, mas não está limitando o número de trabalhos que podem ser enviados e enfileirados para execução posterior. O aplicativo apresentará escassez de recursos posteriormente, mas eventualmente ocorrerá se a taxa de envio ultrapassar consistentemente a taxa de execução.

A solução para esse problema é: Fornecer uma fila de bloqueio ao executor para manter as tarefas em espera. No caso de a fila ficar cheia, a tarefa enviada será "rejeitada". O RejectedExecutionHandleré chamado quando um envio de tarefa é rejeitado e é por isso que o verbo rejeitado foi citado no item anterior. Você pode implementar sua própria política de rejeição ou usar uma das políticas integradas fornecidas pela estrutura.

A política de rejeição padrão faz com que o executor lance um RejectedExecutionException. No entanto, outras políticas integradas permitem que você:

  • Descarte um trabalho silenciosamente.
  • Descarte o trabalho mais antigo e tente reenviar o último.
  • Execute a tarefa rejeitada no encadeamento do chamador.
Prashant Gautam
fonte
5

Fonte

Regras de um tamanho de pool ThreadPoolExecutor

As regras para o tamanho de uma ThreadPoolExecutor'spiscina geralmente não são compreendidas, porque ela não funciona da maneira que você acha que deveria ou da maneira que você deseja.

Veja este exemplo. O tamanho do conjunto de encadeamentos inicial é 1, o tamanho do conjunto principal é 5, o tamanho máximo do conjunto é 10 e a fila é 100.

Maneira da Sun: à medida que as solicitações chegam em threads serão criadas até 5, as tarefas serão adicionadas à fila até chegar a 100. Quando a fila estiver cheia, novos threads serão criados até maxPoolSize. Quando todos os threads estiverem em uso e a fila cheia, as tarefas serão rejeitadas. À medida que a fila diminui, também diminui o número de threads ativos.

Forma antecipada pelo usuário: conforme as solicitações chegam em threads serão criadas até 10, as tarefas serão adicionadas à fila até chegar a 100, momento em que são rejeitadas. O número de threads será renomeado no máximo até que a fila esteja vazia. Quando a fila estiver vazia, os threads morrerão até que corePoolSizesobrem.

A diferença é que os usuários querem começar a aumentar o tamanho do pool mais cedo e querem que a fila seja menor, enquanto o método Sun deseja manter o tamanho do pool pequeno e apenas aumentá-lo quando a carga se tornar muito.

Aqui estão as regras da Sun para a criação de threads em termos simples:

  1. Se o número de threads for menor que o corePoolSize, crie um novo Thread para executar uma nova tarefa.
  2. Se o número de threads for igual (ou maior que) corePoolSize, coloque a tarefa na fila.
  3. Se a fila estiver cheia e o número de threads for menor que o maxPoolSize, crie um novo thread para executar tarefas.
  4. Se a fila estiver cheia e o número de threads for maior ou igual a maxPoolSize, rejeite a tarefa. Resumindo, os novos encadeamentos são criados apenas quando a fila fica cheia, portanto, se você estiver usando uma fila ilimitada, o número de encadeamentos não será superior corePoolSize.

Para uma explicação mais completa, pegue na boca dos cavalos: ThreadPoolExecutordocumentação da API.

Há uma postagem de fórum muito boa que mostra como ThreadPoolExecutorfunciona com exemplos de código: http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0

Mais informações: http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450

Premraj
fonte
3

Você pode encontrar a definição dos termos corepoolsize e maxpoolsize no javadoc. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html

O link acima tem a resposta para sua pergunta. No entanto, só para deixar isso claro. O aplicativo continuará criando threads até atingir corePoolSize. Acho que a ideia aqui é que esses muitos threads devem ser suficientes para lidar com o fluxo de entrada de tarefas. Se uma nova tarefa vier após os threads corePoolSize serem criados, as tarefas serão enfileiradas. Quando a fila estiver cheia, o executor começará a criar novos threads. É uma espécie de equilíbrio. O que essencialmente significa é que o influxo de tarefas é mais do que a capacidade de processamento. Portanto, o Executor começará a criar novos threads novamente até atingir o número máximo de threads. Novamente, um novo encadeamento será criado se e somente se a fila estiver cheia.

Braj
fonte
3

Boa explicação neste blog:

Ilustração

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;
                }
            }
        }
    }
}

Resultado :

---- 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
fonte
1

Do livro Java Concurency Essentials :

CorePoolSize : O ThreadPoolExecutor tem um atributo corePoolSize que determina quantos threads ele iniciará até que novos threads sejam iniciados apenas quando a fila estiver cheia

MaximumPoolSize : este atributo determina quantos threads são iniciados no máximo. Você pode definir isso como Inteiro. MAX_VALUE para não ter limite superior

Ramesh Papaganti
fonte
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);
    }
宏杰 李
fonte
0

Entender o comportamento interno de ThreadPoolExecutorquando uma nova tarefa é enviada me ajudou a entender como corePoolSizee como maximumPoolSizediferir.

Deixei:

  • Nser o número de threads no pool getPoolSize(),. Threads ativos + threads inativos.
  • T ser a quantidade de tarefas submetidas ao executor / pool.
  • Cser o tamanho do pool principal getCorePoolSize(). Quantos threads podem ser criados por pool para as tarefas de entrada antes que novas tarefas vão para a fila .
  • Mser o tamanho máximo do pool getMaximumPoolSize(). Quantidade máxima de threads que o pool pode alocar.

Comportamentos do ThreadPoolExecutorem Java quando uma nova tarefa é enviada:

  • Pois N <= C, os threads ociosos não são atribuídos à nova tarefa de entrada, em vez disso, um novo thread é criado.
  • Para N > Ce se houver threads inativos, uma nova tarefa é atribuída a eles.
  • Para N > Ce se não houver encadeamentos ociosos, novas tarefas são colocadas na fila. NENHUMA NOVA LINHA CRIADA AQUI.
  • Quando a fila está cheia , criamos novos threads até M. Se Mfor alcançado, rejeitamos as tarefas. O que é importante não notar aqui é que não criamos novos threads até que a fila esteja cheia!

Fontes:

Exemplos

Exemplo com corePoolSize = 0e maximumPoolSize = 10com capacidade de fila de 50.

Isso resultará em um único encadeamento ativo no pool até que a fila contenha 50 itens.

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.

Exemplo com corePoolSize = 10e maximumPoolSize = 10com capacidade de fila de 50.

Isso resultará em 10 threads ativos no pool. Quando a fila tiver 50 itens, as tarefas serão rejeitadas.

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`. 
Andrew
fonte