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?
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:
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.
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 threads
consente al pool di thread di scalare tra 0 e max threads
.
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:
Se il numero di thread è inferiore a corePoolSize, creare un nuovo thread per eseguire una nuova attività.
Se il numero di thread è uguale (o maggiore di) corePoolSize, inserire l'attività nella coda.
Se la coda è piena e il numero di thread è inferiore a maxPoolSize, creare un nuovo thread in cui eseguire le attività.
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 ...
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).
Se decidi di crearne uno ThreadPoolExecutor
manualmente invece di usare la Executors
classe 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:
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 RejectedExecutionHandler
viene 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:
Regole di una dimensione del pool ThreadPoolExecutor
Le regole per le dimensioni di una ThreadPoolExecutor's
piscina 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:
corePoolSize
, creare un nuovo thread per eseguire una nuova attività.corePoolSize
, metti l'attività in coda.maxPoolSize
, creare un nuovo thread in cui eseguire le attività.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: ThreadPoolExecutor
documentazione API.
C'è un ottimo post sul forum che ti spiega come ThreadPoolExecutor
funziona 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
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.
Buona spiegazione in questo blog:
public class ThreadPoolExecutorExample {
public static void main (String[] args) {
createAndRunPoolForQueue(new ArrayBlockingQueue<Runnable>(3), "Bounded");
createAndRunPoolForQueue(new LinkedBlockingDeque<>(), "Unbounded");
createAndRunPoolForQueue(new SynchronousQueue<Runnable>(), "Direct hand-off");
}
private static void createAndRunPoolForQueue (BlockingQueue<Runnable> queue,
String msg) {
System.out.println("---- " + msg + " queue instance = " +
queue.getClass()+ " -------------");
ThreadPoolExecutor e = new ThreadPoolExecutor(2, 5, Long.MAX_VALUE,
TimeUnit.NANOSECONDS, queue);
for (int i = 0; i < 10; i++) {
try {
e.execute(new Task());
} catch (RejectedExecutionException ex) {
System.out.println("Task rejected = " + (i + 1));
}
printStatus(i + 1, e);
}
e.shutdownNow();
System.out.println("--------------------\n");
}
private static void printStatus (int taskSubmitted, ThreadPoolExecutor e) {
StringBuilder s = new StringBuilder();
s.append("poolSize = ")
.append(e.getPoolSize())
.append(", corePoolSize = ")
.append(e.getCorePoolSize())
.append(", queueSize = ")
.append(e.getQueue()
.size())
.append(", queueRemainingCapacity = ")
.append(e.getQueue()
.remainingCapacity())
.append(", maximumPoolSize = ")
.append(e.getMaximumPoolSize())
.append(", totalTasksSubmitted = ")
.append(taskSubmitted);
System.out.println(s.toString());
}
private static class Task implements Runnable {
@Override
public void run () {
while (true) {
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
break;
}
}
}
}
}
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
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
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);
}
Comprendere il comportamento interno di ThreadPoolExecutor
quando viene inviata una nuova attività mi ha aiutato a capire come corePoolSize
e maximumPoolSize
differire.
Permettere:
N
il numero di thread nel pool, getPoolSize()
. Thread attivi + thread inattivi.T
essere la quantità di attività inviate all'esecutore / pool.C
la 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 .M
la dimensione massima piscina, getMaximumPoolSize()
. Quantità massima di thread che il pool può allocare.Comportamenti di ThreadPoolExecutor
in Java quando viene inviata una nuova attività:
N <= C
, ai thread inattivi non viene assegnata la nuova attività in arrivo, ma viene creato un nuovo thread.N > C
e se sono presenti thread inattivi, viene assegnata una nuova attività.N > C
e se NON ci sono thread inattivi, le nuove attività vengono inserite nella coda. NESSUN NUOVO FILETTO CREATO QUI.M
. Se M
viene raggiunto, rifiutiamo i compiti. Ciò che è importante non qui è che non creiamo nuovi thread fino a quando la coda non è piena!Fonti:
corePoolSize = 0
e maximumPoolSize = 10
con 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.
corePoolSize = 10
e maximumPoolSize = 10
con 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`.