Cet article étudie principalement l'exemple d'application Code de la condition de blocage de la concurrence Java, comme suit.
La condition décompose les méthodes de moniteur d'objets (attendez, notifiez et notifiez) en objets complètement différents afin qu'en combinant ces objets avec n'importe quelle implémentation de verrouillage, chaque objet fournit plusieurs ensembles d'attente (ensemble d'attente). Parmi eux, Lock remplace l'utilisation des méthodes et des instructions synchronisées et la condition remplace l'utilisation des méthodes de moniteur d'objet.
Étant donné que l'état peut être utilisé pour remplacer l'attente, les notifications et d'autres méthodes, nous pouvons comparer le code de communication inter-thread écrits auparavant et jeter un œil au problème d'origine:
Il y a deux fils. Le thread enfant s'exécute 10 fois d'abord, puis le thread principal s'exécute 5 fois, puis passe au thread enfant exécute 10, puis le thread principal s'exécute 5 fois ... Ce voyage aller-retour est 50 fois.
J'ai utilisé l'attente et le notifier pour l'implémenter auparavant, mais maintenant j'utilise une condition pour la réécrire, le code est le suivant:
classe publique ConditionCommunication {public static void main (String [] args) {business business = new business (); nouveau thread (new Runnable () {// ouvrir un enfant @Override public void run () {for (int i = 1; i <= 50; i ++) {busSiness.Sub (i);}}). 50; {condition.Await (); // Utiliser la condition pour appeler la méthode Await} catch (exception e) {// TODO GÉNÉRATEUR AUTO GÉNÉRÉ BLOCKE.PRINTSTACKTRACE ();}} pour (int j = 1; J <= 10; J ++) {System.out.println ("Sous-thread Sequence de" + j + ", Loop of" + I);} bshouldSub = false; condition.signal (); // utiliser la condition pour envoyer un signal de réveil et réveiller un certain} enfin {lock.unlock ();}} public void main (int i) {lock.lock (); try {while (bsouldSub) {try {condition.await (); // utiliser la condition pour appeler la méthode await} catch (exception e) {// to-generate Blocke.printStackTrace ();}} pour (int j = 1; j <= 10; j ++) {System.out.println ("séquence de threads principaux de" + j + ", boucle de" + i);} bsHouldSub = True; Condition.Signal (); // Utiliser la condition pour envoyer un signal de réveil, réveiller une certaine} enfin {Lock.un.Du point de vue du code, la condition est utilisée avec le verrouillage. Sans verrouillage, la condition ne peut pas être utilisée, car la condition est créée via le verrouillage. Cette utilisation est très simple. Tant que vous maîtrisez l'utilisation des synchronisés, attendons et en informer, vous pouvez maîtriser pleinement l'utilisation du verrouillage et de l'état.
Ce qui précède utilise le verrouillage et la condition au lieu des méthodes synchronisées et du moniteur d'objets pour réaliser la communication entre deux threads. Maintenant, écrivons une application légèrement plus avancée: simulez la file d'attente de blocage du tampon.
Qu'est-ce qu'un tampon? Par exemple, il y a beaucoup de gens qui souhaitent envoyer des messages maintenant. Je suis une station de transit et je veux aider les autres à envoyer des messages. Alors maintenant, j'ai besoin de faire deux choses. Une chose est de recevoir des messages envoyés par l'utilisateur et de les mettre dans le tampon dans l'ordre. L'autre chose est de retirer les messages envoyés par l'utilisateur dans l'ordre dans le tampon et de les envoyer.
Maintenant, abstraitez ce problème réel: un tampon est un tableau. Nous pouvons écrire des données dans le tableau ou retirer les données du tableau. Les deux choses que je dois faire est de démarrer deux threads, l'un pour stocker des données et l'autre pour obtenir des données. Mais le problème est que si le tampon est plein, cela signifie qu'il y a trop de messages reçus, c'est-à-dire que le message envoyé est trop rapide, et un autre fil de ma vie ne suffit pas pour l'envoyer, ce qui entraîne le tampon laissé de côté, de sorte que le fil du stockage de données doit être bloqué et l'attendre; Au contraire, si je le transfère trop rapidement, et maintenant tout le contenu du tampon a été envoyé par moi et aucun nouveau message n'a été envoyé par l'utilisateur, alors le fil de l'acquisition de données doit être bloqué pour le moment.
OK, après avoir analysé la file d'attente de blocage de ce tampon, utilisons la technologie des conditions pour la mettre en œuvre:
tampon de classe {verrouillage final de verrouillage = new rentrantLock (); // définir une condition finale de verrouillage notull = lock.newcondition (); // définir l'objet conditionfinal notempty = lock.newCondition (); // définir l'objet conditionfinal [] items = nouvel objet [10]; // pour la simulation suivante, régler la taille de la file d'attente en bloc Count; // Les indices de tableau sont utilisés pour calibrer les données de position // de stockage dans la file d'attente publique void put (objet x) lance InterruptedException {lock.lock (); // lock try {while (count == Itements.Length) {System.out.println (thread.currentThread (). Être! "); notull.await (); // Si la file d'attente est pleine, bloquez le thread de stockage de données, en attendant d'être réveillé} // s'il n'est pas complet, stockez des éléments [putptr] = x; if (++ putptr == items.length) // c'est le jugement qui atteint la fin du tableau. S'il est atteint, revenez au début putptr = 0; ++ count; // nombre de messages System.out.println (thread.currentThread (). GetName () + "Enregistrer la valeur:" + x); Nothempty.Signal (); // D'accord, il y a maintenant des données dans la file d'attente. Réveillez le thread avec la file d'attente vide et vous pouvez obtenir les données} enfin {lock.unlock (); // télécharger le verrouillage}} // récupérer les données de la file d'attente publique à prendre () lance InterruptedException {lock.lock (); // Lock try {while (count == 0) {System.out.println (thread.currentThread (). Être! "); notEmpty.Await (); // Si la file d'attente est vide, alors le thread bloque les données pour récupérer le thread, en attendant d'être réveillé} // s'il n'est pas vide, prenez l'objet x = articles [Takeptr]; si (++ à prendre) System.out.println (thread.currentThread (). GetName () + "Sortez la valeur:" + x); notull.signal (); // D'accord, maintenant il y a un emplacement dans la file d'attente. Réveillez le fil plein de la file d'attente et vous pouvez stocker des données. Return x;} enfin {lock.unlock (); // lock}}}Ce programme est classique, je l'ai retiré de la documentation officielle JDK et j'ai ajouté des commentaires. Il y a deux conditions définies dans le programme, qui sont utilisées pour exécuter les deux threads, et attendre et se réveiller respectivement. L'idée est claire et le programme est également très robuste. Une question peut être prise en compte, pourquoi avez-vous besoin d'utiliser deux codes? Il doit y avoir une raison pour cette conception. Si vous utilisez une condition, supposons maintenant que la file d'attente est pleine, mais il y a 2 threads A et B qui stockent les données en même temps, alors ils entrent tous le sommeil. Ok, maintenant un autre fil en emporte un, puis réveille l'un des fils A, puis A peut le sauver. Après avoir sauvé, un réveille un autre fil. Si B est éveillé, il y aura un problème, car la file d'attente est pleine pour le moment et B ne peut pas le stocker. Si B stocke, il écrasera la valeur qui n'a pas été récupérée. Parce qu'une condition est utilisée, le stockage et le retrait utilisent cette condition pour dormir et se réveiller, et il sera gâché. À ce stade, vous pouvez comprendre l'utilisation de cette condition. Testons maintenant l'effet de la file d'attente de blocage ci-dessus:
classe publique BoundsedBuffer {public static void main (String [] args) {buffer buffer = new buffer (); for (int i = 0; i <5; i ++) {// ouvrir 5 threads pour stocker les données dans le tampon nouveau thread (new runnable () {@Override public void run () {try {tamper. (InterruptedException e) {e.PrintStackTrace ();}}}). Start ();} pour (int i = 0; i <10; i ++) {// Ouvrir 10 threads pour obtenir des données à partir du tampon nouveau (new Runnable () {@Override public void run () {try {tampon {e.printStackTrace ();}}}). start ();}}}}J'ai délibérément activé seulement 5 threads pour stocker les données et 10 threads pour récupérer les données, juste pour les empêcher d'obtenir des données et voir les résultats de l'opération:
Thread-5 est bloqué et les données ne peuvent pas être récupérées pour le moment!
Le thread-10 est bloqué et les données ne peuvent pas être récupérées pour le moment!
Valeur enregistrée par thread-1: 755
Filetage-0 Valeur enregistrée: 206
Valeur enregistrée par thread-2: 741
Thread-3 Valeur enregistrée: 381
Thread-14 Valeur supprimée: 755
Thread-4 Valeur enregistrée: 783
Thread-6 retirer la valeur: 206
Thread-7 Valeur supprimée: 741
Thread-8 Valeur supprimée: 381
Thread-9 retirer la valeur: 783
Thread-5 est bloqué et les données ne peuvent pas être récupérées pour le moment!
Le thread-11 est bloqué et les données ne peuvent pas être récupérées pour le moment!
Le thread-12 est bloqué et les données ne peuvent pas être récupérées pour le moment!
Le thread-10 est bloqué et les données ne peuvent pas être récupérées pour le moment!
Le thread-13 est bloqué et les données ne peuvent pas être récupérées pour le moment!
D'après les résultats, nous pouvons voir que les threads 5 et 10 sont exécutés en premier, et ils constatent qu'il n'y a pas dans la file d'attente, donc ils sont bloqués et y dorment. Ils ne peuvent l'obtenir que jusqu'à ce qu'une nouvelle valeur soit stockée dans la file d'attente. Cependant, ils n'ont pas de chance et les données stockées sont prises en premier par d'autres threads. Haha ... ils peuvent l'exécuter plusieurs fois. Si vous souhaitez voir les données stockées sont bloquées, vous pouvez définir le thread pour récupérer un peu moins les données et je ne les définirai pas ici.
C'est toujours la même question qu'avant. Maintenant, laissez trois threads l'exécuter. Jetons un coup d'œil à la question:
Il y a trois threads, Child Thread 1 s'exécute 10 fois, le thread de l'enfant 2 s'exécute 10 fois, puis le thread principal s'exécute 5 fois, puis le passage au fil d'enfant 1 exécute 10 fois, le thread enfant 2 s'exécute 10 fois, le thread principal s'exécute 5 fois ... Ce voyage aller-retour est 50 fois.
Si vous n'utilisez pas de condition, c'est vraiment difficile à faire, mais c'est très pratique de le faire avec l'état. Le principe est très simple. Définir trois conditions. Après l'exécution du thread 1 de l'enfant, l'enfant thread 2 réveille le thread principal et le thread principal réveille le fil de l'enfant 1. Le mécanisme de réveil est similaire au tampon ci-dessus. Jetons un coup d'œil au code ci-dessous, il est facile à comprendre.
classe publique Threcondition Communication {public static void main (String [] args) {Business Business = new Business (); nouveau thread (new Runnable () {// ouvrir un enfant thread @Override public void run () {for (int i = 1; i <= 50; i ++) {busssiness.sub1 (i);}}}). {// Démarrer un autre enfant thread @Override public void run () {for (int i = 1; i <= 50; i ++) {busssiness.sub2 (i);}}}). Start (); // méthode principale thread principal pour (int i = 1; i <= 50; i ++) {BUSSNISSE.MAIN (i);}} classe statique {Locker Business = New Reentrant (i);}} STATIC CLASS STATIQUE BUSALITS {Locker) Business (New Reentrant (i);}} STATIC CLASS STATIQUE Clastique statique {Locker Lock = New Reentrant ();}} STATIC CLASS STATIQUE BUSALITS {Locker Lock = New Reentrant (I);}} STATIC CLASS STANC condition1 = lock.newCondition (); // condition est une condition 2 = lock.newCondition (); condition conditionmain = lock.newCondition (); private int bsouldsub = 0; public void sub1 (int i) {lock.lock (); try {while (bshouldSub! = 0) {essai {condition1.await (); // utilise la condition pour appeler la condition} {exception {condition1.await (); // utilise la condition pour appeler la condition} {// TODO Genterated Catch Blocke.printStackTrace ();}} pour (int j = 1; j <= 10; j ++) {System.out.println ("Sub1 Thread Séquence de" + j + " {lock.unlock ();}} public void sub2 (int i) {lock.lock (); try {while (bSouldSub! = 1) {try {condition2.Await (); // Utiliser la condition pour appeler la méthode Await} Catch (exception e) {// toDo Auto-Generated Catch Blocke.printStackTrace ();}}} pour (int J = 1; J <= 10; {System.out.println ("Séquence de threads sub2 de" + j + ", boucle de" + i);} bSouldSub = 2; conditionmain.signal (); // Laissez le thread principal exécuter} enfin {Lock.Unlock () ations 0; condition1.signal (); // Let Thread 1 exécuter} enfin {lock.unlock ();}}}}Le code semble un peu long, mais c'est une illusion et la logique est très simple. C'est tout pour résumer la technologie de condition dans les fils.
Ce qui précède est l'intégralité du contenu de cet article sur l'exemple de code d'application de la condition de blocage de la condition de concurrence Java. J'espère que ce sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à d'autres sujets connexes sur ce site. S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien pour ce site!