Le motif de consommation du producteur est le modèle le plus courant parmi le multi-threading: le fil de producteur (un ou plusieurs) génère du pain et le met dans le panier (set ou tableau), et en même temps, le fil de consommation (un ou plusieurs) retire le pain du panier (set ou tableau) et le consomme. Bien qu'ils aient des tâches différentes, les ressources qu'ils traitent sont les mêmes, ce qui reflète une méthode de communication inter-thread.
Cet article expliquera d'abord la situation des producteurs uniques et des consommateurs uniques, puis expliquera la situation du modèle multi-producteur et multi-consommation. Ces deux modes seront également mis en œuvre à l'aide du mécanisme attendu () / nofity () / nofityall () et le mécanisme Lock () / Unlock () respectivement.
Avant de commencer l'introduction du modèle, expliquez les détails d'utilisation des méthodes d'attente (), de notify () et de notifyall () ainsi que de l'amélioration de l'utilisation de Lock () / Unlock (), Await () / Signal () / SignalLall ().
1. Le principe de mécanisme d'attente et de réveil
attendre (), notifier () et notifyall () représentent respectivement les fils qui entrent dans le sommeil, réveillez le fil de sommeil et réveillez tous les fils de sommeil. Mais, quel thread est l'objet? De plus, les trois méthodes décrites dans la documentation de l'API doivent être utilisées sous la prémisse d'un moniteur valide (qui peut être compris comme tenant une serrure). Qu'est-ce que ces trois méthodes ont à voir avec le verrouillage?
Prendre le bloc de code de synchronisation Synchronisé (OBJ) {} ou les fonctions de synchronisation comme exemple, wait (), notify (), et notifyall () peut être utilisé dans leur structure de code car ils contiennent tous des verrous.
Pour les deux blocs de code de synchronisation suivants, le verrouillage Obj1 et le verrouillage Obj2 sont utilisés respectivement. Le thread 1 et le thread 2 exécutent le code de synchronisation correspondant à Obj1, et le thread 3 et le thread 4 exécutent le code de synchronisation correspondant à l'OBJ2.
classe MyLock implémente Runnable {public int flag = 0; Objet obj1 = nouveau objet (); Objet obj2 = nouveau objet (); public void run () {while (true) {if (Flag% 2 = 0) {synchronisé (obj1) {// threads t1 et t2 effectuer cette tâche de synchronisation // try {obj1.wait ();} catch (interruptedException i) {} //obj1.notify () //obj1.notifyall ()} synchronisé (obj2) {// Thread T3 et T4 Effectuez cette tâche de synchronisation // try {obj2.wait ();} catch (InterruptedException i) {} //obj2.notify () //obj2.Notifyall ()}}}}}}}}}} Class Demo {public static static Main (String (String new mylock (); Thread t1 = nouveau thread (ml); Thread t2 = nouveau thread (ml); Thread t3 = nouveau thread (ml); Thread t4 = nouveau thread (ml); t1.start (); t2.start (); essayez {thread.sleep (1)} catch (InterruptedException i) {}; ml.flag ++; t3.start (); T4.Start (); }}Lorsque T1 commence à s'exécuter pour attendre (), il entrera dans un état de sommeil, mais ce n'est pas un sommeil normal, mais dort dans un pool de threads identifié par OBJ1 (en fait le moniteur correspond au pool de threads, mais le moniteur et le verrouillage sont liés ensemble à ce moment). Lorsque T2 commence à s'exécuter, il constate que le verrouillage Obj1 est maintenu par d'autres threads et il entrera dans un état de sommeil. Cette fois, c'est parce que la ressource de verrouillage attend plutôt que le sommeil entré par attente (). Parce que T2 a déjà déterminé qu'il s'applique pour le verrou OBJ1, il entrera également dans le sommeil de la piscine de fil OBJ1, plutôt que le sommeil ordinaire. De même, T3 et T4, ces deux threads entreront dans la piscine de thread OBJ2 pour dormir.
Lorsqu'un thread s'exécute pour notifier (), cette notification () réveillera au hasard tout fil dans le pool de threads correspondant à son verrou. Par exemple, obj1.notify () réveillera n'importe quel fil de sommeil dans la piscine de fil OBJ1 (bien sûr, s'il n'y a pas de fil de sommeil, ne faites rien). De même, notifyall () réveille tous les fils de sommeil dans le pool de filetage correspondant du verrou.
Ce que vous devez comprendre, c'est le "verrouillage correspondant", car le verrou doit être spécifié explicitement lors de l'appel Wait (), Notify () et NotifyAll (). Par exemple, obj1.wait (). Si la serrure appartient à lui est omise, cela signifie cet objet, c'est-à-dire que les préfixes de ces trois méthodes ne peuvent être omis que dans les fonctions de synchronisation non statiques.
En bref, lorsque la synchronisation est utilisée, le verrouillage est utilisé et le fil a une maison, et toutes ses bases sont déterminées par le verrouillage d'appartenance. Par exemple, lors de la synchronisation du thread, il détermine si le verrou est inactif pour décider d'exécuter le code ultérieur et détermine également s'il faut aller dans un pool de threads spécifique pour dormir. Lors de l'éveil, il ne réveillera que le fil dans le pool de fil correspondant au verrou.
Dans l'application de ces méthodes, généralement dans une tâche, attendre () et notify () / notifyall () apparaissent par paires et exécutent une par une. En d'autres termes, au cours de cette série d'exécution synchrone atomique, Wait () est exécuté pour dormir, ou Notify () est exécuté pour réveiller le fil de sommeil dans la piscine de threads. Pour atteindre une exécution sélective, vous pouvez envisager d'utiliser le marquage comme base de jugement. Reportez-vous aux exemples suivants.
2.Chose et condition
Les trois méthodes de la série Wait () sont très limitées car les actions de sommeil et de réveil sont complètement couplées à la serrure. Par exemple, le thread associé à l'OBJ1 de verrouillage ne peut réveiller le thread dans le pool de thread OBJ1, mais ne peut pas réveiller le fil associé à l'OBJ2 de verrouillage; Par exemple, lorsque la synchronisation synchronisée a été à l'origine synchronisée, le verrou a été implicitement acquis automatiquement lorsque la synchronisation a commencé, et après l'exécution de la tâche entière, il a implicitement publié automatiquement le verrou, ce qui signifie que l'action de l'acquisition du verrou et de la libération du verrouillage n'a pas pu être contrôlée manuellement.
À partir de JDK 1.5, Java fournit le package java.util.concurrent.locks, qui fournit l'interface de verrouillage, l'interface de condition et l'interface readwritelock. Les deux premières interfaces découplent les méthodes de verrouillage et de moniteur (sommeil, opérations de réveil). L'interface de verrouillage ne fournit que des verrous. Grâce à la méthode de verrouillage newconditon (), un ou plusieurs moniteurs associés au verrou peuvent être générés. Chaque moniteur a ses propres méthodes de sommeil et de réveil. En d'autres termes, Lock remplace l'utilisation des méthodes synchronisées et des blocs de code synchronisés, et la condition remplace l'utilisation des méthodes de moniteur d'objet.
Comme indiqué dans la figure ci-dessous:
Lorsqu'un thread exécute Condition1.Await (), le thread entrera le pool de threads correspondant au moniteur Condition1 pour dormir. Lorsque Condition1.Signal () est exécuté, tout fil dans le pool de threads Condition1 sera éveillé au hasard. Lorsque Condition1.Signalall () est exécuté, tous les threads du pool de threads Condition1 seront éveillés. De même, il en va de même pour le moniteur Condition2.
Même s'il y a plusieurs moniteurs, tant qu'ils sont associés au même objet de verrouillage, l'autre thread peut être utilisé sur le moniteur. Par exemple, un thread dans Condition1 peut exécuter Condition2.Signal () pour réveiller un fil dans le pool de threads Condition2.
Pour utiliser ce mode d'association des verrous et des moniteurs, reportez-vous aux étapes suivantes:
Importer java.util.concurrent.locks. *; Lock L = new reentrantLock (); condition con1 = l.newCondition (); condition con2 = l.newCondition (); l.lock (); try {// segment de code contenant Await (), signal () ou signalall () ...} enfin {l.unlock (); // Étant donné que le segment de code peut être anormal, unlock () doit être exécuté, l'essai doit être utilisé et déverrouiller () doit être placé dans le segment enfin}Pour une utilisation spécifique, veuillez consulter l'exemple de code de verrouillage et de condition ultérieurement.
3. Modèle de consommation unique producteur unique
Un fil de producteur, un fil de consommation. Pour chaque pain produit par le producteur, mettez-le dans l'assiette, le consommateur sort le pain de l'assiette pour la consommation. La base des producteurs pour juger de la production de production est qu'il n'y a pas de pain dans l'assiette, tandis que la base des consommateurs pour juger de consommer est qu'il y a du pain dans l'assiette. Étant donné que dans ce mode, une seule miche de pain est toujours placée sur l'assiette, l'assiette peut être omise et le producteur et le consommateur peuvent remettre le pain étape par étape.
Tout d'abord, nous devons décrire ces trois catégories: l'une est la ressource exploitée par plusieurs threads (voici du pain), le second est le producteur, et le troisième est le consommateur. Dans l'exemple suivant, je résume les méthodes de production de pain et de consommation de pain dans les classes productrices et de consommateurs respectivement, ce qui est plus facile à comprendre s'ils sont encapsulés dans la classe de pain.
// Description Ressource: le nom et le nombre de pain, déterminés par le nombre de pain de pain {nom de chaîne publique; public int count = 1; Flag booléen public = false; // Cette marque fournit des marques de jugement pour attendre () et notify ()} // La ressource de pain traitée par le producteur et le consommateur sont les mêmes. Pour vous assurer cela, // la classe de pain peut être conçue en fonction du modèle Singleton, ou le même objet de pain peut être transmis au producteur et au consommateur par le biais de la méthode de construction. Cette dernière méthode est utilisée ici. // Décrivez le producteur de classe producteur Runnable {Privy Bread B; // Membre du producteur: la ressource qu'il souhaite traiter le producteur (pain b) {this.b = b; } // Fournit une méthode pour produire du pain public public produit (nom de chaîne) {b.name = name + b.Count; B.Count ++; } public void run(){ while(true){ synchronized(Bread.class){ //Use Bread.class as the lock identifier so that the synchronized code blocks of producers and consumers can use the same lock if(b.flag){ //wait() must be inside the synchronous code block, not only because the lock must be held to sleep, but there will be confusion in the judgment of the lock resource essayez {pain.class.wait ();} catch (InterruptedException i) {}} produce ("pain"); System.out.println(Thread.currentThread().getName()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // notify () doit également être synchronisé, sinon le verrou a été publié, et l'action de réveil ne peut pas être effectuée // ps: dans une tâche de synchronisation, attendre () et notifier () ne doit être exécuté que le thread de l'autre partie sera confus}}}} // décrire la classe de consommation des consommateurs entièrement en cours d'exécution {pain printe B). this.b = b;} // méthode de la consommation de la consommation de chaîne publique de pain () {return b.name;} public void run () {while (true) {synchronisé (pain.class) {if (! b.flag) {try}} System.out.println(Thread.currentThread().getName()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // 2.Le résultat de l'exécution finale doit être produit et consommé, et il s'agit d'un cycle continu. comme suit:
Thread-0 --- Producteur ---- Bread1read-1 --- Consumer ------- Bread1read-0 --- Producteur ---- Bread2thread-1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4. Utilisez le verrouillage et l'état pour réaliser un modèle de production et de consommation unique
Le code est le suivant:
Importer java.util.concurrent.locks. *; class Bread {public String Name; public int count = 1; Flag booléen public = false; // Fournit le même objet de verrouillage et le même objet de condition pour les producteurs et les consommateurs STATIC STATIC LOCK LOCK = new reentrantLock (); CONDITION STATIQUE STATIQUE PUBLIQUE = LOCK.NEWCONDITION ();} Classe Producteur Implémentez Runnable {Privy Bread B; Producteur (pain b) {this.b = b; } public void produce (String name) {b.name = name + B.Count; B.Count ++; } public void run () {while (true) {// utilise pain.lock pour verrouiller la ressource pain.lock.lock (); try {if (b.flag) {try {pain.condition.Await ();} catch (interruptedException i) {}} produce ("pain"); System.out.println(Thread.currentThread().getName()+"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Bread.Condition. if (! b.flag) {try {pain.condition.await ();} catch (InterruptedException i) {}} System.out.println(Thread.currentThread().getName()+"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // 2.5. Modèle multi-production et consommation (pain unique)
Ici, nous expliquons d'abord le modèle de plusieurs producteurs et multiples consommateurs, mais tout au plus un pain en même temps. Ce modèle n'est peut-être pas idéal dans la réalité, mais pour conduire à la véritable multi-production et à un modèle de consommation multiple plus tard, je pense qu'il est nécessaire d'expliquer ce modèle ici et d'analyser ce modèle et comment il a évolué à partir du code de production unique et de consommation unique.
Comme indiqué dans la figure ci-dessous:
De la production unique et de la consommation unique à la production multiple et à la consommation multiple, en raison de problèmes de sécurité multi-thread et de problèmes de blocage, deux problèmes doivent être pris en compte:
Pour une partie, comment le multi-threading peut-il atteindre la même capacité de production ou de consommation que le jeu unique? En d'autres termes, comment donner à un look multi-threading look unique. La plus grande différence entre le multi-threading et le lancement unique est les problèmes de sécurité multiples. Par conséquent, tant que vous vous assurez que les tâches exécutées par le multi-threading peuvent être synchronisées.
La première question examine le problème du multi-threading sur une partie, et la deuxième question examine comment les deux parties peuvent coopérer harmonieusement pour terminer la production et la consommation. Autrement dit, comment s'assurer qu'un côté du producteur et du consommateur dort tandis que l'autre côté est actif. Réveillez simplement l'autre partie lorsqu'une partie a fini d'effectuer la tâche de synchronisation.
En fait, du fil unique au multi-lancement, il y a deux problèmes qui doivent être pris en compte: hors synchronisation et impasse. (1) Lorsque le producteur et le côté consommateur ont plusieurs threads, les multi-threads du producteur peuvent être considérés comme un fil dans son ensemble, et les multi-threads du côté consommateur également dans son ensemble, ce qui résout le problème de sécurité du fil. (2) La combinaison de l'ensemble de la production et l'ensemble du consommateur est considéré comme multi-lancement pour résoudre le problème de l'impasse. La façon de résoudre l'étalage en Java est de réveiller l'autre partie ou de tout réveiller.
La question est de savoir comment assurer la synchronisation entre plusieurs threads d'une certaine partie? Le code d'un seul consommateur est analysé par exécution multithread comme exemple.
while (true) {synchronisé (pain.class) {if (! b.flag) {try {pain.class.wait ();} catch (interruptedException i) {}} System.out.println (thread.currentThread (). GetName () + "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Supposons que le thread de consommation 1 réveille le fil de consommation 2 après avoir consommé une miche de pain et continue de faire boucle, juger si (! Flag), il attendra et que le verrou est libéré. En supposant que le CPU sélectionne simplement le thread 2 Consumer 2, le thread 2 du consommateur entrera également en attente. Lorsque le producteur produit une miche de pain, supposons que le fil de consommation 1 soit éveillé, il continuera de consommer le pain nouvellement produit à partir de l'énoncé d'attente, supposons que le fil de consommation 2 soit à nouveau éveillé. Lorsque le fil de consommation 2 est sélectionné par le CPU, le fil de consommation 2 consomme également vers le bas par rapport à l'énoncé d'attente, et le pain qui vient d'être produit est consommé. Le problème se pose à nouveau. Les fils de consommation éveillés en continu 1 et 2 consomment le même pain, ce qui signifie que le pain est consommé à plusieurs reprises. Il s'agit d'un autre problème de synchronisation multithread.
Après en avoir parlé pendant longtemps, il est en fait très simple à analyser après avoir élargi la ligne de vue. Tant que les deux fils ou plus d'une partie attendent le jugement B.Flag, les deux ou plusieurs fils peuvent être consacrés en continu et continuer à être produits ou consommés vers le bas. Cela crée le problème de la synchronisation multi-threading.
Le problème de l'insécurité réside dans le fait que plusieurs fils de la même partie continuent de produire ou de consommer vers le bas après un réveil continu. Ceci est causé par la déclaration IF. Si le fil d'attente peut revenir en arrière pour déterminer si B.Flag est vrai après le réveil, il peut décider de continuer à attendre ou à la production ou à la consommation vers le bas.
Vous pouvez remplacer l'instruction IF par une déclaration de temps pour répondre aux exigences. De cette façon, que plusieurs fils de plusieurs fils d'une certaine partie soient continuellement éveillés, ils se retourneront au juge B.Flag.
while (true) {synchronisé (pain.class) {while (! b.flag) {try {pain.class.wait ();} catch (interruptedException i) {}} System.out.println (thread.currentThread (). GetName () + "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Le premier problème de sécurité multithread a été résolu, mais des problèmes de blocage se sont produits. C'est facile à analyser. Le producteur est considéré dans son ensemble et le consommateur est également un tout. Lorsque les fils du producteur attendent tous (les fils de la fête de production sont continuellement éveillés, tous les fils de la fête attendront), et le consommateur attend également et l'impasse apparaîtra. En fait, si vous le regardez de manière amplifiée, le producteur et le consommateur sont considérés respectivement comme un seul fil. Ces deux threads forment plusieurs threads. Quand un côté attend et ne peut pas réveiller l'autre côté, l'autre côté attendra certainement, donc il sera bloqué.
Pour le problème de l'impasse entre les deux parties, tant que vous vous assurez que l'autre partie peut être éveillée, plutôt que le réveil continu de la seule partie, il peut être résolu. Utilisez simplement NotifyAll () ou Signalall (), ou vous pouvez réveiller l'autre thread via Signal () pour résoudre le problème. Voir le deuxième code ci-dessous.
Selon l'analyse ci-dessus, si le code de production unique et un modèle de consommation unique est amélioré, il peut être changé en un modèle de pain unique multi-production et multi-consommation.
// Segment de code 1Class Bread {Nom de chaîne publique; public int count = 1; Flag booléen public = false; } // Décrivez le producteur de classe producteur Runnable {Bread privé B; Producteur (pain b) {this.b = b; } public void produce (String name) {b.name = name + B.Count; B.Count ++; } public void run () {while (true) {synchronisé (pain.class) {while (b.flag) {try {pain.class.wait ();} catch (interruptedException i) {}} produce ("pain"); System.out.println(Thread.currentThread().getName()+"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- String Consomption () {return B.Name;} public void run () {while (true) {synchronisé (pain.class) {while (! System.out.println (thread.currentThread (). GetName () + "----------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- }} Classe publique Produceconsume_5 {public static vide (String [] Args) {// 1. 2 thread con_t1 = nouveau thread (con);Ce qui suit est le code refactorisé à l'aide de Lock et Conditon, en utilisant Signal () pour réveiller l'autre thread.
// Segment de code 2IMPORT Java.util.concurrent.locks. *; Class Bread {public String Name; public int count = 1; Flag booléen public = false; Lock statique de verrouillage statique = new reentrantLock (); Public Static Condition Pro_Con = Lock.NewCondition (); CONDITION STATIQUE PUBLIQUE CON_CON = LOCK.NEWCONTITION ();} // Décrivez le producteur Producteur Implmente Runnable {Private Bread B; Producteur (pain b) {this.b = b; } public void produce (String name) {b.name = name + B.Count; B.Count ++; } public void run () {while (true) {pain.lock.lock (); try {while (b.flag) {try {pain.pro_con.await ();} catch (interruptedException i) {}} produce ("pain"); System.out.println (thread.currentThread (). GetName () + "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Bread.Con_con.Signal (); while (! b.flag) {try {pain.con_con.await ();} catch (InterruptedException i) {}} System.out.println(Thread.currentThread().getName()+"-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //2. Create producer and consumer objects Producer pro = new Producer(b); Consumer con = new Consumer(b); //3. Create thread object Thread pro_t1 = new Thread(pro); Thread pro_t2 = new Thread(pro); Thread con_t1 = new Thread(con); Thread con_t2 = new Thread(con); pro_t1.start(); pro_t2.start(); con_t1.start(); con_t2.start ();}}Résumons les problèmes de plus de production et plus de consommation:
(1). La solution à la sortie de la synchronisation multi-lancement d'une certaine partie est d'utiliser pendant que (drapeau) pour déterminer si l'attente;
(2). La solution au problème de l'impasse des deux parties est de réveiller l'autre partie. Vous pouvez utiliser NotifyAll (), Signalall () ou la méthode Signal () du moniteur de l'autre partie.
6. Plus de modèles de production et de consommation
Il existe plusieurs threads de producteur et plusieurs threads de consommation. Le producteur met le pain produit dans un panier (set ou tableau), et le consommateur sort le pain du panier. La base des producteurs pour juger la production continue est que le panier est plein, et la base pour que les consommateurs jugent la consommation continue est de savoir si le panier est vide. De plus, lorsque le consommateur sort le pain, la position correspondante redevient vide et le producteur peut faire demi-tour et continuer la production à partir de la position de départ du panier, qui peut être réalisée en réinitialisant le pointeur du panier.
Dans ce modèle, en plus de décrire les producteurs, les consommateurs et le pain, il est également nécessaire de décrire le conteneur du panier. Supposons qu'un tableau est utilisé comme conteneur, chaque fois que le producteur en produit un, le pointeur de production se déplace vers l'arrière, et chaque fois que le consommateur en consomme un, le pointeur de consommation se déplace vers l'arrière.
Le code est le suivant: Vous pouvez vous référer à l'exemple de code donné dans l'API -> Classe de condition
import java.util.concurrent.locks. *; Basket de classe {pain privé [] arr; // la taille du panier de panier (taille int) {arr = new pain [taille]; } // Le pointeur de dans et out private int in_ptr, out_ptr; // Combien de pains laissés dans Basket Private int Left; Lock de verrouillage privé = new reentrantLock (); état privé complet = lock.newCondition (); condition privée vide = lock.newCondition (); // pain dans le panier public vide dans () {lock.lock (); try {while (Left == arr.length) {try {full.await ();} catch (interruptedException i) {i.printStackTrace ();}} arr [in_ptr] = new pain ("mianbao", producer.num ++); System.out.println ("Mettez le pain:" + arr [in_ptr] .getName () + "------- dans le panier [" + in_ptr + "]"); gauche ++; if (++ in_ptr == arr.length) {in_ptr = 0;} vide.signal (); } enfin {lock.unlock (); }} // pain hors du panier public Bread Out () {lock.lock (); try {while (Left == 0) {try {vide.await ();} catch (InterruptedException i) {i.printStackTrace ();}} pain Out_bread = arr [out_ptr]; System.out.println ("Obtenez le pain:" + out_bread.getName () + "---------- de Basket [" + out_ptr + "]"); gauche--; if (++ out_ptr == arr.length) {out_ptr = 0;} full.signal (); return out_bread; } enfin {lock.unlock (); }}} classe pain {nom de chaîne privée; Pain (nom de chaîne, int num) {this.name = name + num; } public String getName () {return this.name; }} Le producteur de classe implémente Runnable {Private Basket Basket; public static int num = 1; // Le premier numéro pour le producteur de nom de Bread (panier B) {this.basket = b; } public void run () {while (true) {Basket.in (); Try {Thread.Sleep (10);} Catch (InterruptedException i) {}}}} La classe Consumer implémente Runnable {Basket privé; pain privé i_get; Consommateur (panier b) {this.basket = b; } public void run () {while (true) {i_get = basket.out (); Try {Thread.Sleep (10);} Catch (InterruptedException i) {}}}} public class producconsume_7 {public static void main (String [] args) {Basket b = new Basket (20); // la taille du panier = 20 producteur pro = nouveau producteur (b); Consumer Con = nouveau consommateur (B); Thread pro_t1 = nouveau thread (pro); Thread pro_t2 = nouveau thread (pro); Thread con_t1 = nouveau thread (con); Thread con_t2 = nouveau thread (con); Thread con_t3 = nouveau thread (con); pro_t1.start (); pro_t2.start (); con_t1.start (); con_t2.start (); con_t3.start (); }}Cela implique des consommateurs, des producteurs, du pain et des paniers, où le pain et les paniers sont des ressources exploitées par plusieurs threads. Le fil du producteur produit du pain et le met dans le panier, et le fil de consommation élimine le pain du panier. Le code idéal consiste à résumer à la fois les tâches de production et les tâches de consommation dans la classe de ressources. Parce que le pain est un élément du contenant du panier, il ne convient pas à l'emballage dans la classe de pain, et l'emballage dans le panier facilite le fait de faire fonctionner le récipient.
Notez que vous devez mettre tous les codes impliquant des opérations de ressources à l'intérieur du serrure, sinon un problème de synchronisation multithrefred se produira. Par exemple, la méthode produisant du pain est définie dans la classe du producteur, puis elle est utilisée comme paramètre pour la méthode Basket.in () dans le panier, c'est-à-dire Basket.in (producteur ()), ce qui est un mauvais comportement car le producteur () est transmis à la méthode In () après son exécution en dehors du serrure.
L'article ci-dessus est basé sur le modèle de producteur et de consommation Java (analyse détaillée) et est l'ensemble du contenu partagé par l'éditeur. J'espère que cela pourra vous donner une référence et j'espère que vous pourrez soutenir Wulin.com plus.