Préface
Dans la programmation multi-thread, il n'est pas réaliste d'attribuer un thread à chaque tâche, et la consommation de frais généraux et de ressources de la création de threads est très élevée. Les piscines de threads ont vu le jour et sont devenus un outil puissant pour nous pour gérer les fils. Grâce à l'interface de l'exécuteur, Java fournit une méthode standard pour découpler le processus de soumission de tâche et exécuter le processus, et utilise Runnable pour représenter la tâche.
Ensuite, analysons la mise en œuvre du framework Java Thread Pool ThreadPoolExecutor.
L'analyse suivante est basée sur JDK1.7
cycle de vie
Dans ThreadPoolExecutor , les 3 bits supérieurs de capacité sont utilisés pour représenter l'état de course, qui sont:
1.Running: Recevez de nouvelles tâches et des tâches de traitement dans la file d'attente des tâches
2.Shutdown: des tâches qui ne reçoivent pas de nouvelles tâches mais gérent les files d'attente des tâches
3.STOP: Aucune nouvelle tâche n'est reçue, aucune file d'attente de tâche n'est libérée et toutes les tâches en cours sont interrompues en même temps
4.Tifiing: Toutes les tâches ont été résiliées, le nombre de threads de travail est 0. Lorsque cet état est atteint, terminé () sera exécuté.
5. termination: terminée () a été exécutée
Diagramme de transition d'état
Les classes atomiques sont utilisées pour représenter des bits d'État dans ThreadpoolExecutor
Private Final AtomicInteger CTL = new AtomicInteger (CTLOF (Running, 0));
Modèle de piscine de fil
Paramètres de base
CorePoolSize: Nombre minimum de threads de travailleur vivant (si AllowCoreThreadTimeout est défini, alors cette valeur est 0)
MaximumpoolSize: le nombre maximum de threads, limité par capacité
KeepaliveTime: le temps de survie du fil correspondant, l'unité de temps est spécifiée par TimeUnit
WorkQueue: la file d'attente de travail, le stockage des tâches à exécuter
RejectExecutionHandler: Reject Policy, la capacité maximale du pool de threads sera déclenchée après la pleine pool de threads: les trois premiers bits de capacité sont utilisés comme bits de drapeau, c'est-à-dire que la capacité maximale du fil du travailleur est (2 ^ 29) -1
Quatre modèles
CacheThreadpool: une piscine de fil cacheable. Si la taille actuelle du pool de filetage dépasse les exigences de traitement, le fil inactif sera recyclé. Lorsque la demande augmente, de nouveaux threads peuvent être ajoutés. Il n'y a pas de limite sur la taille du pool de threads.
FIXETHREADPOOL: un pool de threads de taille fixe. Lors de la soumission d'une tâche, un thread est créé jusqu'à ce que le nombre maximum de pools de thread soit atteint. À l'heure actuelle, la taille du pool de thread ne changera plus.
SingleThreadpool: un pool de fil unique, qui n'a qu'un seul fil de travail pour exécuter des tâches. Il peut garantir que les tâches sont exécutées en série dans l'ordre dans lequel ils sont dans la file d'attente. Si ce fil se termine anormalement, un nouveau fil sera créé pour exécuter des tâches.
ScheduledThreadpool: un pool de threads de taille fixe et effectue des tâches de manière retardée ou chronométrée, similaire à la minuterie.
Exécuter l'exécution de la tâche
Logique de base:
1.
2. Nombre actuel de threads> = CorePoolSize, et la tâche est ajoutée avec succès à la file d'attente de travail
1). Vérifiez si l'état actuel du pool de threads est en cours d'exécution
2). Sinon, la tâche est rejetée
3). Si c'est le cas, déterminez si le nombre actuel de threads est 0. S'il est 0, ajoutez un thread de travail.
3. Allumez la tâche d'exécution de thread normal AddWorker (Commande, False) et refusez la tâche si elle ne commence pas. À partir de l'analyse ci-dessus, nous pouvons résumer les quatre étapes du fonctionnement du pool de threads:
1) .PoolSize <CorePoolSize et la file d'attente est vide. Un nouveau fil sera créé pour traiter les tâches soumises.
2) .PoolSize == CorePoolSize. À l'heure actuelle, la tâche soumise entre dans la file d'attente de travail. Le thread de travailleur obtient l'exécution de la tâche à partir de la file d'attente. Pour le moment, la file d'attente n'est pas vide et pas pleine.
3) .PoolSize == CorePoolSize et la file d'attente est pleine. Un nouveau thread sera également créé pour traiter la tâche soumise, mais PoolSize <MaxpoolSize
4) .PoolSize == MaxPoolSize et la file d'attente est pleine, la politique de rejet sera déclenchée
Politique de rejet <br /> Nous avons mentionné plus tôt que si une tâche ne peut pas être exécutée, elle sera rejetée. RejectEdExecutionHandler est l'interface pour gérer les tâches rejetées. Voici quatre stratégies de rejet.
Abortpolicy: politique par défaut, terminer la tâche, lancer RejectEdException
CalleRrunspolicy: Exécutez la tâche actuelle sur le fil de l'appelant sans lancer des exceptions
Discardpolicy: rejette la politique, jetez directement la tâche et ne lancez pas d'exceptions
Di ScandoldersPolicy: Abandonnez la tâche la plus ancienne, exécutez la tâche actuelle et ne lancez pas des exceptions
Travailleur en piscine
Le travailleur hérite de l'abstractqueueEdSynchronizer et Runnable. Le premier fournit au travailleur la fonction de verrouillage et le second exécute la méthode principale des threads de travail Runworker (travailleur W) (exécution de la tâche SNAP à partir de la file d'attente des tâches). Les références des travailleurs se trouvent dans la collection des travailleurs et sont protégées par Mainlock.
private final reentrantlock mainlock = new reentrantLock ();
Private Final Hashset <Dorking> Workers = New HashSet <Dorking> ();
Runworker de fonction de base
Ce qui suit est la logique simplifiée, Remarque: l'exécution de chaque thread de travail exécute les fonctions suivantes
Final void Runworker (Worker W) {Thread wt = Thread.currentThread (); Runnable task = w.firstTask; w.firstTask = null; while (tâche! = null || (task = getTask ())! = null) {w.lock (); avant Execute (wt, tâche); tâche.run (); AfterExecute (tâche, lancé); w.unlock (); } processWorkErexit (w, complété);} 1. Obtenez la tâche de getTask ()
2. Verrouiller le travailleur
3. Exécuter avant Execute (WT, tâche), qui est la méthode d'extension fournie par ThreadPoolExecutor aux sous-classes
4. Exécutez la tâche. Si le travailleur a configuré la première tâche, la première tâche sera exécutée en premier et une seule fois.
5. Exécuter AfterExecute (tâche, lancé);
6. Déverrouiller le travailleur
7. Si la tâche obtenue est nul, fermez le travailleur
Obtenez la tâche GetTask
La file d'attente des tâches à l'intérieur du pool de threads est une file d'attente de blocage, qui est transmise pendant la construction.
BlockingQueue finale privée <Chunnable> WorkQueue;
getTask () tire la tâche de la file d'attente des tâches, prend en charge le blocage et le délai d'attente en attente de tâches. Quatre situations entraîneront le retour nul et le travailleur est fermé.
1. Le nombre de threads existants dépasse le nombre maximum de threads
2. La piscine de fil est à l'état d'arrêt
3. Le pool de threads est à l'état d'arrêt et la file d'attente de travail est vide
4. Time d'attente du thread Temps, et le nombre de threads dépasse le nombre de threads conservés
Logique de base: a chronométré ou bloqué la tâche d'attente dans la file d'attente de blocage. La tâche d'attente chronométrée entraînera la fermeture du fil du travailleur.
timed = allowCoreThreadTimeout || wc> corepoolSize; runnable r = timed? workQueue.poll (keepalivetime, timeunit.nanoseconds): workqueue.take ();
Attendre une tâche sera désactivée dans deux cas:
1. Permettez aux threads de noyau d'attendre le délai d'attente, c'est-à-dire, pertetethreadtimeout (true)
2. Le thread actuel est un thread normal, à ce moment WC> CorePoolSize
La file d'attente de travail utilise BlockingQueue, donc je ne le développerai pas ici. J'écrirai une analyse détaillée plus tard.
Résumer
ThreadPoolExecutor est basé sur le modèle producteur-consommateur. Le fonctionnement des tâches de soumission est équivalent au producteur, et le fil d'exécution des tâches est équivalent au consommateur.
Les exécuteurs fournissent quatre méthodes pour construire un modèle de pool de threads basé sur ThreadPoolExecutor. De plus, nous pouvons hériter directement de ThreadPoolExecutor et réécrire avant Execute et AfterExecute des méthodes pour personnaliser le processus d'exécution des tâches de pool de threads.
L'utilisation de files d'attente limitée ou de files d'attente illimitées doit être prise en compte en fonction de la situation spécifique, et la taille de la file d'attente de travail et le nombre de threads doivent également être soigneusement considérés.
La politique de rejet est recommandée d'utiliser CalleRrunspolicy, qui n'abandonne pas la tâche ou ne lance pas une exception, mais remonte à la place la tâche au fil de l'appelant pour l'exécution.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.