D'une manière générale, la vitesse des tâches de production est supérieure à la vitesse de consommation. Une question de détail est la longueur de file d'attente et comment correspondre à la vitesse de production et de consommation.
Un modèle de producteur-consommateur typique est le suivant:
Car la production générale est plus rapide que la consommation. Lorsque la file d'attente est complète, nous ne voulons pas que aucune tâche soit ignorée ou non exécutée. . Le blocage est également facile.
De plus, lorsque la file d'attente est vide, les consommateurs ne peuvent pas obtenir la tâche, ils peuvent attendre un certain temps avant de l'obtenir. . De cette façon, lorsque le producteur a effectivement arrêté la production, les consommateurs n'attendront pas l'infini.
Par conséquent, un modèle efficace de production et de consommation soutenant le blocage est mis en œuvre.
Attendez, puisque JUC nous a aidés à implémenter des pools de threads, pourquoi devons-nous encore utiliser cet ensemble de choses? N'est-il pas plus pratique d'utiliser directement le service d'exécution?
Jetons un coup d'œil à la structure de base de ThreadpoolExecutor:
Mais le problème est que même si vous spécifiez manuellement un BlockingQueue en tant qu'implémentation de file d'attente lors de la construction de ThreadPoolExecutor, en fait, lorsque la file d'attente est pleine, la méthode d'exécution ne bloque pas.
La copie de code est la suivante:
public void execute (Commande Runnable) {
if (Command == null)
lancer un nouveau nullpointerException ();
if (poolSize> = corePoolSize ||! AddiFunderCorePoolSize (commande)) {
if (runState == Running && workqueue.offer (commande)) {
if (RunState! = Running || PoolSize == 0)
assurez-vous de faire en sorte que la commande (commande);
}
else if (! addifunderMaxiMumpoolSize (commande))
rejeter (commande);
}
}
Pour le moment, quelque chose doit être fait pour obtenir un résultat: lorsque le producteur soumet une tâche et que la file d'attente est pleine, le producteur peut le bloquer et attendre que la tâche soit consommée.
La clé est que dans un environnement simultané, la file d'attente ne peut pas être jugée par le producteur, et ThreadpoolExecutor.getQueue (). Size () ne peut pas être appelé pour déterminer si la file d'attente est pleine.
Dans la mise en œuvre du pool de threads, lorsque la file d'attente est pleine, le rejetdexecutionhandler transmis pendant la construction sera appelé pour rejeter le traitement de la tâche. L'implémentation par défaut est AbortPolicy, qui lance directement un rejetdexecutionException.
Je n'entrerai pas dans les détails de plusieurs stratégies de rejet ici. La consommation.
La copie de code est la suivante:
classe statique publique CallErrunspolicy implémente rejettedexecutionhandler {
/ **
* Crée un <TT> CalleRrunspolicy </TT>.
* /
public CalleRrunSpolicy () {}
/ **
* Exécute la tâche R dans le thread de l'appelant, sauf si l'exécuteur
* a été fermé, auquel cas la tâche est rejetée.
* @param r La tâche runnable demandée à exécuter
* @param e l'exécuteur testamentaire qui tente d'exécuter cette tâche
* /
public void rejectEdExecution (runnable r, threadpoolexecutor e) {
if (! e.isshutdown ()) {
R.Run ();
}
}
}
Cependant, cette stratégie a également des dangers cachés. Continuez à produire les tâches.
Se référant à des idées similaires, de la manière la plus simple, nous pouvons définir directement un rejetdexecutionhandler, et le changer pour appeler BlockingQueue.publique lorsque la file d'attente est pleine pour réaliser le blocage du producteur:
La copie de code est la suivante:
Nouveau rejettedExecutionHandler () {
@Outrepasser
public void rejectEdExecution (Runnable R, ThreadPoolExecutor Exécuteur) {
if (! exécutor.isshutdown ()) {
essayer {
Executor.getQueue (). put (r);
} catch (InterruptedException e) {
// ne devrait pas être interrompu
}
}
}
};
De cette façon, nous n'avons plus besoin de nous soucier de la logique de la file d'attente et du consommateur.
Par rapport à la conception d'origine, cette méthode peut réduire la quantité de code et peut éviter de nombreux problèmes dans des environnements simultanés. Bien sûr, vous pouvez également utiliser d'autres moyens, tels que l'utilisation de sémaphores comme limites d'entrée lors de la soumission, mais si vous voulez simplement bloquer le producteur, cela semblera compliqué.