Taille du pool de base vs taille maximale du pool dans ThreadPoolExecutor

Réponses:

131

De cet article de blog :

Prenons cet exemple. La taille du pool de threads de départ est 1, la taille du pool principal est de 5, la taille maximale du pool est de 10 et la file d'attente est de 100.

Au fur et à mesure que les demandes arrivent, les threads seront créés jusqu'à 5, puis les tâches seront ajoutées à la file d'attente jusqu'à ce qu'elle atteigne 100. Lorsque la file d'attente est pleine, de nouveaux threads seront créés jusqu'à maxPoolSize. Une fois que tous les threads sont utilisés et que la file d'attente est pleine, les tâches seront rejetées. À mesure que la file d'attente se réduit, le nombre de threads actifs diminue également.

user2568266
la source
Est-ce correct? Je pensais que de nouveaux threads seraient créés jusqu'à ce qu'il atteigne maxPoolSize. Ensuite, tous les nouveaux threads seront mis dans la file d'attente. Veuillez me corriger si je me trompe ..
Glide
4
Oui, c'est correct. Les threads ne seront ajoutés au-delà de corePoolSize que s'il y a des tâches dans la file d'attente. Ces threads supplémentaires «mourront» après que la file d'attente atteigne zéro.
Luke
3
Il existe une méthode intéressante allowCoreThreadTimeOut(boolean)qui permet de tuer les threads principaux après un temps d'inactivité donné. Définir ceci sur true et définir core threads= max threadspermet au pool de threads de s'échelonner entre 0 et max threads.
Jaroslaw Pawlak
4
Vous venez de le copier d'ici bigsoft.co.uk/blog/index.php/2009/11/27/…
Kumar Manish
1
Qu'arrive-t-il aux tâches rejetées?
Cire le
54

Si vous exécutez des threads> corePoolSize & <maxPoolSize , créez un nouveau thread si la file d'attente de tâches Total est pleine et qu'une nouvelle arrive.

Form doc: (S'il y a plus de threads corePoolSize mais moins que maximumPoolSize en cours d'exécution, un nouveau thread sera créé uniquement si la file d'attente est pleine.)

Maintenant, prenons un exemple simple,

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

Ici, 5 est le corePoolSize - signifie que Jvm créera un nouveau thread pour une nouvelle tâche pour les 5 premières tâches. et d'autres tâches seront ajoutées à la file d'attente jusqu'à ce que la file d'attente soit pleine (50 tâches).

10 est le maxPoolSize - JVM peut créer un maximum de 10 threads. Signifie que s'il y a déjà 5 tâches / threads en cours d'exécution et que la file d'attente est pleine de 50 tâches en attente et si une nouvelle demande / tâche supplémentaire arrive dans la file d'attente, JVM créera un nouveau thread jusqu'à 10 (nombre total de threads = 5 précédents + nouveau 5) ;

new ArrayBlockingQueue (50) = est une taille totale de file d'attente - il peut y mettre 50 tâches en file d'attente.

une fois que les 10 threads sont en cours d'exécution et si une nouvelle tâche arrive, cette nouvelle tâche sera rejetée.

Règles de création de threads en interne par SUN:

  1. Si le nombre de threads est inférieur à corePoolSize, créez un nouveau Thread pour exécuter une nouvelle tâche.

  2. Si le nombre de threads est égal (ou supérieur) à corePoolSize, placez la tâche dans la file d'attente.

  3. Si la file d'attente est pleine et que le nombre de threads est inférieur à maxPoolSize, créez un nouveau thread pour exécuter les tâches.

  4. Si la file d'attente est pleine et que le nombre de threads est supérieur ou égal à maxPoolSize, rejetez la tâche.

J'espère que c'est utile ... et s'il vous plaît, corrigez-moi si je me trompe ...

Darshan Dalwadi
la source
21

Du doc :

Lorsqu'une nouvelle tâche est soumise dans method execute (java.lang.Runnable) et que moins de threads corePoolSize sont en cours d'exécution, un nouveau thread est créé pour gérer la demande, même si d'autres threads de travail sont inactifs. S'il y a plus de threads corePoolSize mais moins que maximumPoolSize en cours d'exécution, un nouveau thread sera créé uniquement si la file d'attente est pleine.

En outre:

En définissant de la même manière corePoolSize et maximumPoolSize, vous créez un pool de threads de taille fixe. En définissant maximumPoolSize sur une valeur essentiellement illimitée telle que Integer.MAX_VALUE, vous autorisez le pool à accueillir un nombre arbitraire de tâches simultanées. Le plus généralement, les tailles de pool de base et maximum sont définies uniquement lors de la construction, mais elles peuvent également être modifiées dynamiquement à l'aide de setCorePoolSize (int) et setMaximumPoolSize (int).

Brian Agnew
la source
1) Lorsqu'une nouvelle tâche est soumise dans method execute (java.lang.Runnable) et que moins de threads corePoolSize sont en cours d'exécution, un nouveau thread est créé pour gérer la demande, même si d'autres threads de travail sont inactifs. Pourquoi est-il nécessaire de créer un nouveau thread pour gérer la demande s'il y a des threads inactifs?
user2568266
1
2) S'il y a plus de threads corePoolSize mais moins que maximumPoolSize en cours d'exécution, un nouveau thread ne sera créé que si la file d'attente est pleine. Je ne comprends pas la différence entre corePoolSize et maximumPoolSize ici. Deuxièmement, comment une file d'attente peut-elle être pleine lorsque les threads sont inférieurs à maximumPoolSize? La file d'attente ne peut être pleine que si les threads sont égaux à maximumPoolSize. N'est-ce pas?
user2568266
9

Si vous décidez de créer un ThreadPoolExecutormanuellement au lieu d'utiliser la Executorsclasse de fabrique, vous devrez en créer et en configurer un à l'aide de l'un de ses constructeurs. Le constructeur le plus étendu de cette classe est:

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

Comme vous pouvez le voir, vous pouvez configurer:

  • La taille du pool de base (la taille avec laquelle le pool de threads tentera de rester).
  • La taille maximale de la piscine.
  • Le temps de maintien en vie, qui est un temps après lequel un thread inactif est éligible pour être supprimé.
  • La file d'attente de travail pour contenir les tâches en attente d'exécution.
  • La stratégie à appliquer lorsqu'une soumission de tâche est rejetée.

Limitation du nombre de tâches en file d'attente

Limiter le nombre de tâches simultanées en cours d'exécution, dimensionner votre pool de threads, représente un énorme avantage pour votre application et son environnement d'exécution en termes de prévisibilité et de stabilité: une création de thread illimitée finira par épuiser les ressources d'exécution et votre application pourrait en subir les conséquences. , de graves problèmes de performances pouvant même entraîner une instabilité de l'application.

C'est une solution à une seule partie du problème: vous limitez le nombre de tâches en cours d'exécution mais ne limitez pas le nombre de travaux qui peuvent être soumis et mis en file d'attente pour une exécution ultérieure. L'application connaîtra une pénurie de ressources plus tard, mais elle finira par en souffrir si le taux de soumission dépasse systématiquement le taux d'exécution.

La solution à ce problème est: Fournir une file d'attente de blocage à l'exécuteur pour contenir les tâches en attente. Dans le cas où la file d'attente se remplit, la tâche soumise sera "rejetée". Le RejectedExecutionHandlerest appelé lorsqu'une soumission de tâche est rejetée, et c'est pourquoi le verbe rejeté a été cité dans l'élément précédent. Vous pouvez mettre en œuvre votre propre politique de rejet ou utiliser l'une des politiques intégrées fournies par l'infrastructure.

Les politiques de rejet par défaut obligent l'exécuteur à lancer un RejectedExecutionException. Cependant, d'autres stratégies intégrées vous permettent:

  • Supprimez un travail en silence.
  • Supprimez la tâche la plus ancienne et essayez de soumettre à nouveau la dernière.
  • Exécutez la tâche rejetée sur le thread de l'appelant.
Prashant Gautam
la source
6

La source

Règles d'une taille de pool ThreadPoolExecutor

Les règles relatives à la taille d'une ThreadPoolExecutor'spiscine sont généralement mal comprises, car cela ne fonctionne pas comme vous le pensez ou comme vous le souhaitez.

Prenons cet exemple. La taille du pool de threads de départ est 1, la taille du pool principal est de 5, la taille maximale du pool est de 10 et la file d'attente est de 100.

La manière de Sun: au fur et à mesure que les requêtes arrivent, les threads seront créés jusqu'à 5, puis les tâches seront ajoutées à la file d'attente jusqu'à ce qu'elle atteigne 100. Lorsque la file d'attente est pleine, de nouveaux threads seront créés jusqu'à maxPoolSize . Une fois que tous les threads sont utilisés et que la file d'attente est pleine, les tâches seront rejetées. À mesure que la file d'attente diminue, le nombre de threads actifs diminue également.

Manière prévue par l'utilisateur: au fur et à mesure que les demandes arrivent, les threads seront créés jusqu'à 10, puis les tâches seront ajoutées à la file d'attente jusqu'à ce qu'elle atteigne 100, point auquel elles sont rejetées. Le nombre de threads sera renommé au maximum jusqu'à ce que la file d'attente soit vide. Lorsque la file d'attente est vide, les threads mourront jusqu'à ce qu'il en corePoolSizereste.

La différence est que les utilisateurs veulent commencer à augmenter la taille du pool plus tôt et veulent que la file d'attente soit plus petite, alors que la méthode Sun veut garder la taille du pool petite et ne l'augmenter que lorsque la charge devient trop importante.

Voici les règles de Sun pour la création de threads en termes simples:

  1. Si le nombre de threads est inférieur à corePoolSize, créez un nouveau thread pour exécuter une nouvelle tâche.
  2. Si le nombre de threads est égal (ou supérieur) à corePoolSize, placez la tâche dans la file d'attente.
  3. Si la file d'attente est pleine et que le nombre de threads est inférieur à maxPoolSize, créez un nouveau thread pour exécuter les tâches.
  4. Si la file d'attente est pleine et que le nombre de threads est supérieur ou égal à maxPoolSize, rejetez la tâche. Le long et court est que de nouveaux threads ne sont créés que lorsque la file d'attente se remplit, donc si vous utilisez une file d'attente illimitée, le nombre de threads ne dépassera pas corePoolSize.

Pour une explication plus complète, obtenez-le de la bouche du cheval: ThreadPoolExecutordocumentation de l'API.

Il y a un très bon message sur le forum qui vous explique comment cela ThreadPoolExecutorfonctionne avec des exemples de code: http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0

Plus d'infos: http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450

Premraj
la source
3

Vous pouvez trouver la définition des termes corepoolsize et maxpoolsize dans le javadoc. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html

Le lien ci-dessus a la réponse à votre question. Cependant, juste pour que ce soit clair. L'application continuera à créer des threads jusqu'à ce qu'elle atteigne corePoolSize. Je pense que l'idée ici est que ces nombreux threads devraient être suffisants pour gérer l'afflux de tâches. Si une nouvelle tâche survient après la création des threads corePoolSize, les tâches seront mises en file d'attente. Une fois la file d'attente pleine, l'exécuteur commencera à créer de nouveaux threads. C'est une sorte d'équilibre. Cela signifie essentiellement que l'afflux de tâches est supérieur à la capacité de traitement. Ainsi, Executor recommencera à créer de nouveaux threads jusqu'à ce qu'il atteigne le nombre maximum de threads. Encore une fois, un nouveau thread sera créé si et seulement si la file d'attente est pleine.

Braj
la source
3

Bonne explication dans ce blog:

Illustration

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

Production :

---- 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
la source
1

Extrait du livre Java concurency essentials :

CorePoolSize : Le ThreadPoolExecutor a un attribut corePoolSize qui détermine le nombre de threads qu'il démarrera jusqu'à ce que les nouveaux threads ne soient démarrés que lorsque la file d'attente est pleine

MaximumPoolSize : cet attribut détermine combien de threads sont démarrés au maximum. Vous pouvez le définir sur Integer. MAX_VALUE pour ne pas avoir de limite supérieure

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

Comprendre le comportement interne du ThreadPoolExecutorlorsqu'une nouvelle tâche est soumise m'a aidé à comprendre comment corePoolSizeet maximumPoolSizedifférer.

Laisser:

  • Nle nombre de fils dans la piscine, getPoolSize(). Threads actifs + threads inactifs.
  • T être la quantité de tâches soumises à l'exécuteur / pool.
  • Cla taille du pool de base, getCorePoolSize(). Combien de threads au maximum peuvent être créés par pool pour les tâches entrantes avant que les nouvelles tâches ne soient placées dans la file d'attente .
  • Mla taille de la piscine maximale, getMaximumPoolSize(). Nombre maximum de threads que le pool peut allouer.

Comportements de ThreadPoolExecutoren Java lorsqu'une nouvelle tâche est soumise:

  • Pour N <= C, les threads inactifs ne reçoivent pas la nouvelle tâche entrante, mais un nouveau thread est créé.
  • Pour N > C et s'il y a des threads inactifs, une nouvelle tâche y est affectée.
  • Pour N > Cet s'il n'y a AUCUN thread inactif, de nouvelles tâches sont placées dans la file d'attente. AUCUN NOUVEAU FIL CRÉÉ ICI.
  • Lorsque la file d'attente est pleine , nous créons de nouveaux threads jusqu'à M. Si Mest atteint, nous rejetons les tâches. Ce qui est important de ne pas ici, c'est que nous ne créons pas de nouveaux threads tant que la file d'attente n'est pas pleine!

Sources:

Exemples

Exemple avec corePoolSize = 0et maximumPoolSize = 10avec une capacité de file d'attente de50 .

Cela se traduira par un seul thread actif dans le pool jusqu'à ce que la file d'attente contienne 50 éléments.

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.

Exemple avec corePoolSize = 10et maximumPoolSize = 10avec une capacité de file d'attente de50 .

Cela entraînera 10 threads actifs dans le pool. Lorsque la file d'attente contient 50 éléments, les tâches sont rejetées.

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
la source