Les fils sont souvent impliqués dans le travail. Par exemple, certaines tâches sont souvent transmises aux fils pour l'exécution asynchrone. Ou le programme serveur établit une tâche de traitement de thread distincte pour chaque demande. Au-delà des threads, tels que la connexion de la base de données que nous utilisons. Ces opérations de création, de destruction ou d'ouverture et de clôture ont un grand impact sur les performances du système. Par conséquent, l'utilisation de "pool" est mise en évidence.
1. Pourquoi utiliser la piscine de threads
Dans l'implémentation décrite dans la section 3.6.1, un nouveau thread de travailleur est affecté à chaque client. Lorsque le fil du travailleur communique avec le client, le fil est détruit. Cette méthode d'implémentation a les lacunes suivantes:
• Les frais généraux de la création et de la destruction du serveur (y compris les ressources de temps et de système) sont très élevés. Cet élément n'a pas besoin d'être expliqué, vous pouvez vérifier le "processus de création de thread". En plus du travail effectué par la machine elle-même, nous devons également instancier et démarrer, qui nécessitent tous une occupation des ressources de pile.
• En plus des frais généraux de création et de destruction de threads, les threads actifs consomment également des ressources système. Cela devrait être une consommation de ressources de pile. Il est également considéré comme deviner le nombre de connexions de base de données.
• Si le nombre de threads est fixe et que chaque filetage a un long cycle de déclaration, la commutation du thread est également relativement fixe. Différents systèmes d'exploitation ont des cycles de commutation différents, généralement autour de 20 ms. Le commutateur mentionné ici est de transférer les droits d'utilisation du CPU entre les threads dans le cadre de la planification de JVM et du système d'exploitation sous-jacent. Si les threads sont fréquemment créés et détruits, les threads seront commutés fréquemment, car une fois un thread détruit, le droit d'utilisation doit être donné au fil prêt, afin que le thread puisse avoir une chance de fonctionner. Dans ce cas, la commutation entre les threads ne suit pas le cycle de commutation fixe du système, et les frais généraux des threads de commutation sont encore plus importants que les frais généraux de création et de destruction.
Relativement parlant, à l'aide de pools de threads, certains threads sont pré-créés, qui suppriment constamment les tâches de la file d'attente de travail, puis exécutent la tâche. Lorsque le thread de travailleur termine une tâche, il continue d'exécuter une autre tâche dans la file d'attente de travail. Les avantages sont les suivants:
• Réduit le nombre de création et de destruction, et chaque fil de travail peut être réutilisé tout le temps et peut effectuer plusieurs tâches.
• Le nombre de threads dans le pool de threads peut être facilement ajusté en fonction de la capacité de charge du système pour empêcher les accidents du système en raison de ressources système excessives.
2. Implémentation simple du pool de threads
Vous trouverez ci-dessous un simple pool de thread écrit par moi-même, qui était également directement tiré du livre de programmation Java Network
Thread de package; Importer Java.util.LinkedList; / ** * Implémentation du pool de threads, en fonction de la longueur, de la longueur maximale et de la longueur de file d'attente du pool de threads ordinaire, nous pouvons augmenter le nombre d'implémentations * @author han * / public class MyThreadpool étend ThreadGroup {// CPU nombre --- runtime.getRuntime (). Disponible Processers (); // s'il faut fermer le booléen privé isClosed = false; // file d'attente privé LinkedList <Nonnenable> workQueue; // Thread Pool ID Private Static int threadpoolID; INT privé ThreadID; public myThreadpool (int poolSize) {super ("mythreadpool." + threadpoolId); ThreadPoolId ++; setDaemon (true); workQueue = new LinkedList <Nonnenable> (); for (int i = 0; i <poolSize; i ++) {new Workthread (). start (); }} // Ici, vous pouvez passer à ConcurrentLinkEdQueue pour éviter l'efficacité de l'utilisation du public synchronisé synchronisé d'exécution (tâche Runnable) {if (isClosed) {lancez new illégalstateException ("Le pool de connexion a été fermé ..."); } else {workQueue.add (tâche); notify (); }} Protégé Synchronisé Runnable GetTask () lève InterruptedException {while (workQueue.size () == 0) {if (isClosed) {return null; } attendez(); } return workQueue.RemoveFirst (); } public synchronisé void close () {if (! isClosed) {isClosed = true; workQueue.Clear (); interrompre(); }} public void join () {synchronisé (this) {isClosed = true; notifyall (); } Thread [] threads = nouveau thread [activeCount ()]; int count = énumération (threads); for (int i = 0; i <count; i ++) {try {threads [i] .join (); } catch (exception e) {}}} classe WorkThread étend Thread {public workthread () {super (mythreadpool.this, "workthread" + (threadid ++)); System.out.println ("Create ..."); } @Override public void run () {while (! IsInterrupted ()) {System.out.println ("run .."); Tâche de runnable = null; essayez {// c'est une tâche de méthode de blocage = getTask (); } catch (exception e) {} if (tâche! = null) {task.run (); } else {break; }}}}}}Ce pool de threads définit principalement une file d'attente de travail et certains threads pré-créés. Tant que la méthode d'exécution est appelée, vous pouvez soumettre des tâches au fil.
Lorsqu'il n'y a pas de tâche, le thread suivant bloquera dans getTask () jusqu'à ce qu'une nouvelle tâche arrive et soit éveillée.
La jointure et la fermeture peuvent être utilisées pour fermer le pool de threads. La différence est que JOIN effectuera les tâches dans la file d'attente, tandis que Close effacera immédiatement la file d'attente et interrompra tous les threads de travailleur. L'interrupt () dans close () équivaut à appeler l'interrupteur respectif () contenant des threads enfants dans ThreadGroup. Par conséquent, lorsqu'un fil attend ou dorme, une interruptexception sera lancée.
La classe de test est la suivante:
classe publique TestMyThreadPool {public static void main (String [] args) lève InterruptedException {myThreadpool pool = new MyThreadpool (3); for (int i = 0; i <10; i ++) {pool.execute (new Runnable () {@Override public void run () {try {thread.sleep (1000);} catch (interruptedException e) {} system.out.println ("working ...");}}); } pool.join (); //pool.close (); }}3. Le pool de threads fourni par la bibliothèque de classe JDK
Java fournit une bonne implémentation de pool de threads, qui est plus robuste et efficace que notre propre implémentation, et a également des fonctions plus puissantes.
Le diagramme de classe est le suivant:
Les aînés ont déjà donné une bonne explication sur ce type de pool de fils. Tout pool de fils Java sous Baidu a des exemples et des tutoriels très détaillés, donc je ne les répéterai pas ici.
4. Pool de filetage d'injection de printemps
Lorsque nous utilisons le framework Spring, si nous utilisons les méthodes fournies par Java pour créer un pool de threads, il est très gênant de gérer dans des applications multi-thread et n'est pas conforme à notre idée d'utiliser le printemps. (Bien que le printemps puisse être injecté par des méthodes statiques)
En fait, Spring lui-même fournit également une bonne implémentation des pools de threads. Cette classe s'appelle ThreadpoolTasKexecutor.
La configuration au printemps est la suivante:
<bean id = "ExecutorService"> <propriété name = "corepoolSize" value = "$ {threadpool.corepoolSize}" /> <! - Nombre minimum de threads maintenus par Thread Pool -> <propriété name = "keepaliveseconds" a permis de permettre de temps libre pour les threads de filetage pour les filetages de thread name = "maxpoolSize" value = "$ {threadpool.maxpoolSize}" /> <! - Nombre maximum de threads maintenus par Thread Pool -> <propriété named = "queueCapacity" Value = "$ {Threadpool.queuecapacity}" /> <!5. Notes sur l'utilisation des pools de threads
•Impasse
Tout programme multithread a le risque de décomposition. Le cas le plus simple est que deux threads AB, un verrouillage 1, demande le verrouillage 2, B conserve le verrouillage 2 et le verrouillage de demande 1. (Cette situation apparaîtra également dans MySQL Ecclusive Lock, et la base de données rapportera directement un message d'erreur). Il y a une autre impasse dans le pool de threads: en supposant que tous les threads de travail dans le pool de threads sont bloqués lors de l'exécution de leurs tâches respectives, et ils attendent le résultat d'exécution d'une certaine tâche A. La tâche a est dans la file d'attente et ne peut pas être exécutée car il n'y a pas de threads inactifs. De cette façon, toutes les ressources du pool de thread seront bloquées et des impasses seront générées.
• Ressources système insuffisantes
Si le nombre de threads dans le pool de threads est très important, ces threads consommeront une grande quantité de ressources, y compris la mémoire et d'autres ressources système, ce qui affecte sérieusement les performances du système.
• Erreur simultanée
La file d'attente de travail du pool de threads repose sur les méthodes d'attente () et de notifier () pour permettre au travailleur d'obtenir des tâches en temps opportun, mais ces deux méthodes sont difficiles à utiliser. Si le code est erroné, des notifications peuvent être perdues, ce qui a permis au thread des travailleurs de rester inactif, en ignorant les tâches qui doivent être traitées dans la file d'attente de travail. Parce qu'il est préférable d'utiliser des piscines de fil plus matures.
• Fuite de filetage
Un risque grave d'utiliser des pools de threads est la fuite de threads. Pour les pools de threads avec un nombre fixe de threads de travailleur, si le thread de travail lance une conception ou une erreur RuntimeExex lors de l'exécution d'une tâche, et que ces exceptions ou erreurs ne sont pas capturées, le thread de travailleur se termine anormalement, ce qui fait que le pool de threads perd en permanence un fil. (C'est tellement intéressant)
Une autre situation est que le fil du travailleur est bloqué lors de l'exécution d'une tâche. S'il attend les données d'entrée de l'utilisateur, mais l'utilisateur ne saisit pas les données, ce qui entraîne le thread bloqué tout le temps. Un tel thread de travailleur est uniquement dans le nom, et il n'effectue pas de tâches. Si tous les threads du pool de threads sont dans cet état, le pool de thread ne peut pas ajouter de nouvelles tâches.
• Surcharge de tâche
Lorsqu'il y a un grand nombre de tâches en file d'attente à exécuter dans la file d'attente de threads, ces tâches elles-mêmes peuvent consommer trop de ressources système et provoquer des pénuries de ressources.
Pour résumer, lors de l'utilisation de pools de threads, les principes suivants doivent être suivis:
1. Si la tâche A doit attendre le résultat de l'exécution de la tâche B de manière synchrone pendant l'exécution, la tâche A ne convient pas pour être ajoutée à la file d'attente de travail du pool de threads. Si vous devez attendre que d'autres tâches exécutent des résultats comme la tâche A pour être ajoutée à la file d'attente, cela peut provoquer une impasse
2. Si une tâche peut être bloquée et bloquée pendant longtemps, le délai d'expiration doit être réglé pour éviter le blocage permanent du fil du travailleur et provoquer une fuite de fil. Dans le programme de serveur, lorsqu'un thread attend que le client se connecte ou attend les données envoyées par le client, elle peut entraîner un blocage. Vous pouvez définir l'heure de la manière suivante:
Appelez la méthode SETSOTIMEOut de SERVERSOCKET pour définir l'heure du délai d'expiration pour attendre que le client se connecte.
Pour chaque prise connectée au client, appelez la méthode SetSoTimeout de la prise pour définir le délai d'attente en attendant que le client envoie des données.
3. Comprendre les caractéristiques de la tâche et analyser si la tâche effectue des opérations qui bloquent souvent les opérations IO ou effectuent des opérations qui ne bloqueront pas. Le premier occupe le processeur par intermittence, tandis que le second a un taux d'utilisation plus élevé. Combien de temps faut-il pour accomplir une tâche? Est-ce une tâche à court terme ou une tâche à long terme? Ensuite, classez les tâches en fonction des caractéristiques de la tâche, puis ajoutez différents types de tâches à la file d'attente de travail de différents pools de threads. De cette façon, chaque pool de fils peut être alloué et ajusté en fonction des caractéristiques de la tâche.
4. Redimensionner le pool de threads. La taille optimale d'un pool de fils dépend principalement du nombre de processeurs disponibles dans le système et des caractéristiques des tâches dans la file d'attente de travail. S'il n'y a qu'une seule file d'attente de travail sur un système avec N CPU, et que toutes sont des tâches de nature arithmétique (pas de blocage), alors lorsque le pool de threads a des threads de travail N ou N + 1, l'utilisation maximale du processeur sera généralement obtenue.
Si la file d'attente de travail contient des tâches qui effectuent des opérations IO et bloquent souvent, faites en sorte que la taille du pool de threads dépasse le nombre de processeurs disponibles, car tous les threads de travail ne fonctionnent pas tout le temps. Sélectionnez une tâche typique, puis estimez le rapport du temps d'attente jusqu'au temps qui occupe réellement CPU pour effectuer des opérations dans le projet qui effectue cette tâche. Pour un système avec N CPU, environ N * (1 + WT / ST) doivent être définis pour s'assurer que le CPU est entièrement utilisé.
Bien sûr, l'utilisation du processeur n'est pas la seule chose à considérer dans le processus d'ajustement des pools de threads. Au fur et à mesure que le nombre de travaux de pool de thread augmente, la mémoire ou d'autres restrictions de ressources se produiront également, telles que les prises, les poignées de fichiers ouvertes ou les connexions de base de données, etc. Il est nécessaire de garantir que les ressources système consommées par les multithes sont dans le cadre de l'endurance du système.
5. Évitez la surcharge de la tâche. Le serveur doit limiter le nombre de connexions simultanées des clients en fonction de la capacité de charge du système. Lorsque la connexion du client dépasse la valeur limite, le serveur peut rejeter la connexion et créer des invites amicales ou limiter la longueur de la file d'attente.
Les méthodes et réponses de mise en œuvre ci-dessus aux pools de threads Java sont tout le contenu que j'ai partagé avec vous. J'espère que vous pourrez vous faire référence et j'espère que vous pourrez soutenir Wulin.com plus.