Quelle est exactement la différence entre la taille du pool principal et la taille maximale du pool lorsque nous parlons de ThreadPoolExecutor? Peut-il être expliqué à l'aide d'un exemple?
De cet article de blog :
Prenons cet exemple. La taille du pool d'unités d'exécution de départ est 1 et la taille du pool principal est 5, la taille maximale du pool est 10 et la file d'attente est 100.
Au fur et à mesure que les demandes arrivent, les threads seront créés jusqu’à 5, puis des tâches seront ajoutées au fichier file d'attente jusqu'à atteindre 100. Lorsque la file d'attente est pleine, les nouveaux threads deviennent créé à
maxPoolSize
. Une fois que tous les threads sont utilisés et le la file d'attente est pleine, les tâches seront rejetées. À mesure que la file d'attente diminue, il en va de même pour le nombre de threads actifs.
De le doc :
Lorsqu'une nouvelle tâche est soumise dans la méthode execute (Java.lang.Runnable), et 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 que corePoolSize mais moins que maximumPoolSize threads en cours d'exécution, un nouveau thread ne sera créé que si la file d'attente est plein.
En outre:
En définissant corePoolSize et maximumPoolSize de la même manière, vous créez un pool de threads de taille fixe. En définissant maximumPoolSize sur essentiellement valeur non liée telle que Integer.MAX_VALUE, vous autorisez le pool à accueillir un nombre arbitraire de tâches simultanées. Plus généralement, Les tailles de piscine centrale et maximale sont définies uniquement lors de la construction, mais elles peut aussi être changé dynamiquement en utilisant setCorePoolSize (int) et setMaximumPoolSize (int).
IF threads en cours d'exécution> corePoolSize & <maxPoolSize , puis créez un nouveau thread si la file d'attente de tâches Total est saturée et qu'une nouvelle file d'attente arrive.
Form doc: (S'il y a plus de corePoolSize mais moins que maximumPoolSize threads en cours d'exécution, un nouveau thread ne sera créé que si la file d'attente est saturée.)
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 fil 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 celle-ci 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 avec 50 tâches en attente et si une nouvelle requête/tâche 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 de file d'attente totale - il peut mettre en file d'attente 50 tâches.
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 pour la création de threads en interne par Sun:
Si le nombre de threads est inférieur à corePoolSize, créez un nouveau thread pour exécuter une nouvelle tâche.
Si le nombre de threads est égal (ou supérieur) à corePoolSize, placez la tâche dans la file d'attente.
Si la file d'attente est saturée et que le nombre de threads est inférieur à maxPoolSize, créez un nouveau thread pour exécuter des tâches.
Si la file d'attente est saturée et que le nombre de threads est supérieur ou égal à maxPoolSize, refusez la tâche.
Espoir, c'est utile .. et corrigez-moi s'il vous plaît si je me trompe ...
Si vous décidez de créer manuellement une ThreadPoolExecutor
au lieu d'utiliser la classe d'usine Executors
, vous devrez en créer et en utiliser une à 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:
Limiter le nombre de tâches simultanées en cours d’exécution et 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 threads sans limites épuisera à terme 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é des applications.
C'est une solution à un seul aspect du problème: vous limitez le nombre de tâches en cours d'exécution, mais pas le nombre de travaux pouvant être soumis et mis en file d'attente pour une exécution ultérieure. L’application subira ultérieurement une pénurie de ressources, mais elle le sera éventuellement si le taux de soumission dépasse systématiquement le taux d’exécution.
La solution à ce problème est la suivante: Fournir une file d'attente bloquante à l'exécuteur pour contenir les tâches en attente. Si la file d'attente est pleine, la tâche soumise sera "rejetée" . La RejectedExecutionHandler
est invoquée lorsqu'une soumission de tâche est rejetée, raison pour laquelle le verbe rejeté a été cité dans l'élément précédent. Vous pouvez mettre en œuvre votre propre stratégie de rejet ou utiliser l'une des stratégies intégrées fournies par la structure.
Dans le cas des stratégies de rejet par défaut, l'exécuteur renvoie une variable RejectedExecutionException
. Cependant, d'autres stratégies intégrées vous permettent:
Vous pouvez trouver la définition des termes corepoolsize et maxpoolsize dans le fichier 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 le 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 sont mises en file d'attente. Une fois la file d'attente pleine, l'exécuteur commencera à créer de nouveaux threads. C'est un peu équilibré. Cela signifie essentiellement que l'afflux de tâches dépasse la capacité de traitement. Ainsi, Executor va recommencer à créer de nouveaux threads jusqu'à atteindre le nombre maximum de threads. De nouveau, un nouveau thread sera créé si et seulement si la file est pleine.
Bonne explication dans this 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;
}
}
}
}
}
Sortie:
---- 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
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);
}
Extrait du livre Les bases de la concurence Java:
CorePoolSize: ThreadPoolExecutor possède un attribut corePoolSize qui détermine le nombre de threads qu'il démarrera jusqu'à ce que de nouveaux threads ne soient démarrés que lorsque la file d'attente est pleine.
MaximumPoolSize: cet attribut détermine le nombre maximal de threads démarrés. Vous pouvez définir ceci sur Integer. MAX_VALUE afin de ne pas avoir de limite supérieure