Dimensione del pool principale rispetto alla dimensione massima del pool in ThreadPoolExecutor


105

Che cosa è esattamente la differenza tra le dimensioni del pool di base e la massima dimensione della piscina quando si parla in termini di ThreadPoolExecutor?
Può essere spiegato con l'aiuto di un esempio?



Risposte:


130

Da questo post del blog :

Prendi questo esempio. La dimensione del pool di thread iniziale è 1, la dimensione del pool principale è 5, la dimensione massima del pool è 10 e la coda è 100.

All'arrivo delle richieste, verranno creati fino a 5 thread e quindi le attività verranno aggiunte alla coda fino a raggiungere 100. Quando la coda sarà piena, verranno creati nuovi thread fino a maxPoolSize. Una volta che tutti i thread sono in uso e la coda è piena, le attività verranno rifiutate. Man mano che la coda si riduce, anche il numero di thread attivi si riduce.


È corretto? Ho pensato che verranno creati nuovi thread fino a raggiungere maxPoolSize. Quindi eventuali nuovi thread verranno messi in coda. Per favore correggimi se sbaglio ..
Glide

4
Sì, è corretto. I thread verranno aggiunti solo oltre corePoolSize se sono presenti attività nella coda. Questi thread aggiuntivi "moriranno" dopo che la coda raggiunge lo zero.
Luca

3
Esiste un metodo interessante allowCoreThreadTimeOut(boolean)che consente di terminare i thread principali dopo un determinato periodo di inattività. L'impostazione di questo su true e l'impostazione core threads= max threadsconsente al pool di thread di scalare tra 0 e max threads.
Jaroslaw Pawlak


1
Cosa succede alle attività rifiutate?
Cera

54

SE i thread in esecuzione> corePoolSize & <maxPoolSize , creare un nuovo thread se la coda attività totale è piena e ne sta arrivando una nuova.

Documento del modulo: (se sono in esecuzione più thread di corePoolSize ma meno di maximumPoolSize , verrà creato un nuovo thread solo se la coda è piena.)

Ora, fai un semplice esempio,

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

Qui, 5 è corePoolSize - significa che Jvm creerà un nuovo thread per una nuova attività per le prime 5 attività. e altre attività verranno aggiunte alla coda finché la coda non sarà piena (50 attività).

10 è il maxPoolSize - JVM può creare max 10 thread. Significa che se ci sono già 5 attività / thread in esecuzione e la coda è piena di 50 attività in sospeso e se un'altra nuova richiesta / attività sta arrivando in coda, JVM creerà un nuovo thread fino a 10 (thread totali = 5 precedenti + nuovi 5) ;

new ArrayBlockingQueue (50) = è una dimensione totale della coda - può accodare 50 attività in essa.

una volta che tutti i 10 thread sono in esecuzione e se è in arrivo una nuova attività, la nuova attività verrà rifiutata.

Regole per la creazione di thread internamente da SUN:

  1. Se il numero di thread è inferiore a corePoolSize, creare un nuovo thread per eseguire una nuova attività.

  2. Se il numero di thread è uguale (o maggiore di) corePoolSize, inserire l'attività nella coda.

  3. Se la coda è piena e il numero di thread è inferiore a maxPoolSize, creare un nuovo thread in cui eseguire le attività.

  4. Se la coda è piena e il numero di thread è maggiore o uguale a maxPoolSize, rifiutare l'attività.

Spero, questo è d'aiuto .. e per favore correggimi se sbaglio ...


21

Dal doc :

Quando una nuova attività viene inoltrata nel metodo execute (java.lang.Runnable) e sono in esecuzione meno di corePoolSize thread, viene creato un nuovo thread per gestire la richiesta, anche se altri thread di lavoro sono inattivi. Se sono in esecuzione più thread di corePoolSize ma meno di maximumPoolSize, verrà creato un nuovo thread solo se la coda è piena.

Inoltre:

Impostando corePoolSize e maximumPoolSize allo stesso modo, si crea un pool di thread di dimensioni fisse. Impostando maximumPoolSize su un valore essenzialmente illimitato come Integer.MAX_VALUE, si consente al pool di accogliere un numero arbitrario di attività simultanee. In genere, le dimensioni core e massime del pool vengono impostate solo durante la costruzione, ma possono anche essere modificate dinamicamente utilizzando setCorePoolSize (int) e setMaximumPoolSize (int).


1) Quando una nuova attività viene inviata nel metodo execute (java.lang.Runnable) e sono in esecuzione meno thread di corePoolSize, viene creato un nuovo thread per gestire la richiesta, anche se altri thread di lavoro sono inattivi. Perché è necessario creare un nuovo thread per gestire la richiesta se ci sono thread inattivi?
user2568266

1
2) Se sono in esecuzione più thread di corePoolSize ma meno di maximumPoolSize, verrà creato un nuovo thread solo se la coda è piena. Non capisco la differenza tra corePoolSize e maximumPoolSize qui. In secondo luogo, come può una coda è piena quando i thread sono inferiori a maximumPoolSize? La coda può essere piena solo se i thread sono uguali a maximumPoolSize. Non è vero?
user2568266

9

Se decidi di crearne uno ThreadPoolExecutormanualmente invece di usare la Executorsclasse factory, dovrai crearne e configurarne uno usando uno dei suoi costruttori. Il costruttore più completo di questa classe è:

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

Come puoi vedere, puoi configurare:

  • La dimensione del pool di base (la dimensione con cui il pool di thread cercherà di restare fedele).
  • La dimensione massima della piscina.
  • Il tempo di mantenimento, che è un tempo dopo il quale un thread inattivo può essere abbattuto.
  • La coda di lavoro per contenere le attività in attesa di esecuzione.
  • Il criterio da applicare quando l'invio di un'attività viene rifiutato.

Limitazione del numero di attività in coda

Limitare il numero di attività simultanee in esecuzione, dimensionare il pool di thread, rappresenta un enorme vantaggio per la tua applicazione e il suo ambiente di esecuzione in termini di prevedibilità e stabilità: una creazione di thread illimitata finirà per esaurire le risorse di runtime e la tua applicazione potrebbe subire di conseguenza , seri problemi di prestazioni che possono portare anche a instabilità dell'applicazione.

Questa è una soluzione a solo una parte del problema: stai limitando il numero di attività in esecuzione ma non stai limitando il numero di lavori che possono essere inviati e accodati per l'esecuzione successiva. L'applicazione subirà una carenza di risorse in un secondo momento, ma alla fine si verificherà se la velocità di invio supera costantemente la velocità di esecuzione.

La soluzione a questo problema è: Fornire una coda di blocco all'esecutore per mantenere le attività in attesa. Nel caso in cui la coda si riempia, l'attività inviata verrà "rifiutata". Il RejectedExecutionHandlerviene richiamato quando una sottomissione compito viene rifiutata, ed è per questo il verbo respinto è stato citato nella voce precedente. È possibile implementare la propria politica di rifiuto o utilizzare una delle politiche integrate fornite dal framework.

Le politiche di rifiuto predefinite prevedono che l'esecutore lanci un file RejectedExecutionException. Tuttavia, altri criteri integrati ti consentono di:

  • Elimina un lavoro in silenzio.
  • Elimina il lavoro più vecchio e prova a inviare nuovamente l'ultimo.
  • Eseguire l'attività rifiutata sul thread del chiamante.

5

fonte

Regole di una dimensione del pool ThreadPoolExecutor

Le regole per le dimensioni di una ThreadPoolExecutor'spiscina sono generalmente incomprese, perché non funziona nel modo in cui pensi che dovrebbe o nel modo in cui desideri.

Prendi questo esempio. La dimensione del pool di thread iniziale è 1, la dimensione del pool principale è 5, la dimensione massima del pool è 10 e la coda è 100.

Secondo Sun: quando le richieste arrivano nei thread, verranno create fino a 5, quindi le attività verranno aggiunte alla coda fino a raggiungere 100. Quando la coda è piena, verranno creati nuovi thread fino a maxPoolSize. Una volta che tutti i thread sono in uso e la coda è piena, le attività verranno rifiutate. Man mano che la coda si riduce, il numero di thread attivi si riduce.

Modalità anticipata dall'utente: quando le richieste arrivano nei thread, verranno create fino a 10, quindi le attività verranno aggiunte alla coda fino a raggiungere 100, a quel punto vengono rifiutate. Il numero di thread verrà rinominato al massimo finché la coda non sarà vuota. Quando la coda è vuota, i thread moriranno finché non ne rimarranno corePoolSize.

La differenza è che gli utenti vogliono iniziare ad aumentare la dimensione del pool prima e vogliono che la coda sia più piccola, mentre il metodo Sun vuole mantenere la dimensione del pool piccola e aumentarla solo quando il carico diventa eccessivo.

Ecco le regole di Sun per la creazione di thread in termini semplici:

  1. Se il numero di thread è inferiore a corePoolSize, creare un nuovo thread per eseguire una nuova attività.
  2. Se il numero di thread è uguale (o maggiore di) corePoolSize, metti l'attività in coda.
  3. Se la coda è piena e il numero di thread è inferiore a maxPoolSize, creare un nuovo thread in cui eseguire le attività.
  4. Se la coda è piena e il numero di thread è maggiore o uguale a maxPoolSize, rifiutare l'attività. Il lungo e il breve è che i nuovi thread vengono creati solo quando la coda si riempie, quindi se stai utilizzando una coda illimitata, il numero di thread non supererà corePoolSize.

Per una spiegazione più completa, scaricala dalla bocca dei cavalli: ThreadPoolExecutordocumentazione API.

C'è un ottimo post sul forum che ti spiega come ThreadPoolExecutorfunziona con esempi di codice: http://forums.sun.com/thread.jspa?threadID=5401400&tstart=0

Maggiori informazioni: http://forums.sun.com/thread.jspa?threadID=5224557&tstart=450


3

Puoi trovare la definizione dei termini corepoolsize e maxpoolsize nel javadoc. http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html

Il link qui sopra contiene la risposta alla tua domanda. Tuttavia, solo per chiarire. L'applicazione continuerà a creare thread fino a raggiungere corePoolSize. Penso che l'idea qui sia che questi molti thread dovrebbero essere sufficienti per gestire l'afflusso di attività. Se una nuova attività arriva dopo la creazione dei thread corePoolSize, le attività verranno accodate. Una volta che la coda è piena, l'esecutore inizierà a creare nuovi thread. È una specie di bilanciamento. Ciò che essenzialmente significa è che l'afflusso di attività è maggiore della capacità di elaborazione. Quindi, Executor inizierà a creare di nuovo nuovi thread fino a raggiungere il numero massimo di thread. Anche in questo caso, verrà creato un nuovo thread se e solo se la coda è piena.


3

Buona spiegazione in questo blog:

Illustrazione

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

Produzione :

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

1

Dal libro Java concurency essentials :

CorePoolSize : ThreadPoolExecutor ha un attributo corePoolSize che determina quanti thread inizierà fino a quando i nuovi thread non verranno avviati solo quando la coda è piena

MaximumPoolSize : questo attributo determina il numero massimo di thread avviati. Puoi impostarlo su Integer. MAX_VALUE per non avere un limite superiore


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

0

Comprendere il comportamento interno di ThreadPoolExecutorquando viene inviata una nuova attività mi ha aiutato a capire come corePoolSizee maximumPoolSizedifferire.

Permettere:

  • Nil numero di thread nel pool, getPoolSize(). Thread attivi + thread inattivi.
  • T essere la quantità di attività inviate all'esecutore / pool.
  • Cla dimensione nucleo piscina, getCorePoolSize(). Quanti thread possono essere creati al massimo per pool per le attività in arrivo prima che le nuove attività vadano in coda .
  • Mla dimensione massima piscina, getMaximumPoolSize(). Quantità massima di thread che il pool può allocare.

Comportamenti di ThreadPoolExecutorin Java quando viene inviata una nuova attività:

  • Infatti N <= C, ai thread inattivi non viene assegnata la nuova attività in arrivo, ma viene creato un nuovo thread.
  • Per N > Ce se sono presenti thread inattivi, viene assegnata una nuova attività.
  • Per N > Ce se NON ci sono thread inattivi, le nuove attività vengono inserite nella coda. NESSUN NUOVO FILETTO CREATO QUI.
  • Quando la coda è piena , creiamo nuovi thread fino a M. Se Mviene raggiunto, rifiutiamo i compiti. Ciò che è importante non qui è che non creiamo nuovi thread fino a quando la coda non è piena!

Fonti:

Esempi

Esempio con corePoolSize = 0e maximumPoolSize = 10con una capacità di coda di 50.

Ciò risulterà in un singolo thread attivo nel pool finché la coda non avrà 50 elementi in esso.

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.

Esempio con corePoolSize = 10e maximumPoolSize = 10con una capacità di coda di 50.

Ciò comporterà 10 thread attivi nel pool. Quando la coda contiene 50 elementi, le attività verranno rifiutate.

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`. 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.