Maison > Java > javaDidacticiel > le corps du texte

Communication inter-thread Java attendre/notifier

巴扎黑
Libérer: 2017-06-26 11:38:57
original
1458 Les gens l'ont consulté

Wait/notify/notifyAll en Java peut être utilisé pour implémenter la communication inter-thread et est une méthode de la classe Object. Ces trois méthodes sont toutes des méthodes natives et sont souvent utilisées pour implémenter le producteur/. modèle de consommation. Tout d'abord, jetons un coup d'œil aux définitions pertinentes :

 wait() : le thread appelant cette méthode entre dans l'état WATTING et ne reviendra que s'il attend une notification ou une interruption. depuis un autre thread. Appel Après la méthode wait(), le verrou de l'objet sera libéré.

Attendre (longue) : Attendez jusqu'à de longues millisecondes pour l'expiration du délai. S'il n'y a pas de notification, elle expirera et reviendra.

Notify() : Notifie un thread en attente de retour de l'objet à partir de la méthode wait(), et la prémisse du retour est que le thread obtient l'objet verrouillage.

 notifyAll() : Notifie tous les threads en attente sur l'objet.

Un petit exemple

Simulons un exemple simple pour illustrer. Nous avons un petit restaurant de boulettes en bas. L'entreprise est en plein essor et il y a un chef dans le magasin. , un serveur, afin d'éviter qu'à chaque fois que le chef prépare une portion, le serveur en retire une portion, ce qui est trop inefficace et gaspille de l'énergie physique. Supposons maintenant que chaque fois que le chef prépare 10 portions, le serveur les servira au client sur une grande assiette en bois. Après avoir vendu 100 portions chaque jour, le restaurant fermera et le chef et les serveurs rentreront chez eux pour se reposer.

Pensez-y, si vous n'utilisez pas le mécanisme d'attente/notification pour implémenter cette fonction, alors le moyen le plus direct peut être que le serveur aille à la cuisine de temps en temps et sorte le plats dans une assiette après 10 portions. Cette méthode présente deux gros inconvénients :

1. Si le serveur va trop assidûment à la cuisine et qu'il est trop fatigué, il vaut mieux servir un bol à chaque fois qu'il le prépare. Pour les invités, le rôle de la grande assiette en bois ne sera pas reflété. La manifestation spécifique au niveau du code d’implémentation est qu’elle nécessite un bouclage continu et gaspille les ressources du processeur.

2. Si le serveur se rend à la cuisine pour vérifier après une longue période, la rapidité ne peut pas être garantie. Peut-être que le chef a déjà préparé 10 portions, mais le. le serveur n'a pas observé.

Pour l'exemple ci-dessus, il est beaucoup plus raisonnable d'utiliser le mécanisme d'attente/notification. Chaque fois que le chef prépare 10 portions, il crie "Les raviolis sont. prêt, d’accord. Emportez-le ». Après que le serveur ait reçu la notification, il se rend à la cuisine pour servir les raviolis aux invités ; le chef n'en a pas encore fait assez, c'est-à-dire qu'il n'a pas reçu la notification du chef, il peut donc se reposer un peu mais il doit encore tendre l'oreille et attendre la notification du chef .

 

 1 package ConcurrentTest; 2  3 import thread.BlockQueue; 4  5 /** 6  * Created by chengxiao on 2017/6/17. 7  */ 8 public class JiaoziDemo { 9     //创建个共享对象做监视器用10     private static Object obj = new Object();11     //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。12     private static Integer platter = 0;13     //卖出的饺子总量,卖够100份就打烊收工14     private static Integer count = 0;15 16     /**17      * 厨师18      */19     static class Cook implements Runnable{20         @Override21         public void run() {22             while(count<100){23                 synchronized (obj){24                     while (platter<10){25                         platter++;26                     }27                     //通知服务员饺子好了,可以端走了28                     obj.notify();29                     System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿");30                 }31                 try {32                     //线程睡一会,帮助服务员线程抢到对象锁33                     Thread.sleep(100);34                 } catch (InterruptedException e) {35                     e.printStackTrace();36                 }37             }38             System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家");39         }40     }41 42     /**43      * 服务员44      */45     static class Waiter implements Runnable{46         @Override47         public void run() {48             while(count<100){49                 synchronized (obj){50                     //厨师做够10份了,就可以端出去了51                     while(platter < 10){52                         try {53                             System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知...");54                             obj.wait();55                             BlockQueue56                         } catch (InterruptedException e) {57                             e.printStackTrace();58                         }59                     }60                     //饺子端给客人了,盘子清空61                     platter-=10;62                     //又卖出去10份。63                     count+=10;64                     System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了");65                 }66             }67             System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家");68 69         }70     }71     public static void main(String []args){72         Thread cookThread = new Thread(new Cook(),"cookThread");73         Thread waiterThread = new Thread(new Waiter(),"waiterThread");74         cookThread.start();75         waiterThread.start();76     }77 }
Copier après la connexion
Un petit exemple

Résultat d'exécution

cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--打烊收工,服务员回家
cookThread--打烊收工,厨师回家
Copier après la connexion
Exécuter les résultats

Mécanisme de fonctionnement

Empruntez une image de "The Art of Concurrent Programming" pour comprendre le mécanisme de fonctionnement de l'attente/notification

Quelqu'un sait peut-être que je Je ne sais pas grand chose sur ce qu'on appelle le moniteur (monitor) et le verrouillage d'objet (lock). Voici une explication simple :

  jvm associe un verrou à chaque objet et classe Verrouiller un objet signifie obtenir le moniteur associé à l’objet.

Ce n'est que lorsque le verrou de l'objet est acquis que le moniteur peut être obtenu. Si l'acquisition du verrou échoue, le thread entrera dans la file d'attente de blocage ; réussit Après avoir obtenu le verrouillage de l'objet, vous pouvez également utiliser la méthode wait() pour attendre sur le moniteur. À ce moment, le verrou sera libéré et entré dans la file d'attente.

Concernant la différence entre les serrures et les moniteurs, un copain du jardin a écrit un article très détaillé et approfondi que je cite ici pour ceux que ça intéresse. regardez la différence entre les verrous et les moniteurs - Concurrence Java

Sur la base de la figure ci-dessus, trions le processus spécifique

1. Tout d'abord, waitThread acquiert le verrou d'objet, puis appelle la méthode wait(). À ce moment, le thread d'attente abandonnera le verrou d'objet et entrera le file d'attente WaitQueue中;

2. Le thread notifyThread saisit le verrou de l'objet, effectue certaines opérations et appelle la méthode notify() À ce moment, le thread en attente. waitThread sera déplacé de la file d'attente WaitQueue vers la synchronisation Dans la file SynchronizedQueue, waitThread passe de l'état d'attente à l'état bloqué. Il convient de noter que notifyThread ne libérera pas le verrou immédiatement pour le moment Il continuera à fonctionner et ne libérera le verrou qu'après avoir terminé le reste de son travail ;

.

3. waitThread acquiert à nouveau le verrou d'objet, revient de la méthode wait() et continue d'effectuer les opérations suivantes

4. Le processus de communication inter-thread basé sur le ; le mécanisme d’attente/notification se termine.

Comme pour notifyAll, dans la deuxième étape, tous les threads de la

file d'attente sont déplacés vers la file d'attente de synchronisation.

Évitez les pièges

Il y a quelques considérations particulières lors de l'utilisation de wait/notify/notifyAll :

1. wait()/notify()/notifyAll() est synchronisé, ce qui signifie que vous devez d'abord acquérir le verrou. Nous l'avons déjà mentionné, car le moniteur ne peut être obtenu qu'après le verrouillage. Sinon, jvm lancera également IllegalMonitorStateException.

2. Lors de l'utilisation de wait(), la condition pour déterminer si le thread entre dans l'état d'attente doit utiliser while au lieu de if, car le thread en attente peut être fait une erreur de se réveiller, vous devez donc utiliser une boucle while pour vérifier si les conditions de réveil sont remplies avant d'attendre et après avoir attendu pour assurer la sécurité.

3. Une fois la méthode notify() ou notifyAll() appelée, le thread ne libérera pas le verrou immédiatement. L'appel déplacera uniquement le fil en attente de la file d'attente vers la file d'attente de synchronisation, c'est-à-dire que le statut du fil passe d'en attente à bloqué

 4. De wait() Le principe du retour de la méthode est que le thread retrouve le verrou de l'objet appelant.

Postscript

 Il s'agit de l'introduction du contenu lié à l'attente/notification. Dans l'utilisation réelle, une attention particulière doit être accordée au A mentionné ci-dessus. quelques points, mais d'une manière générale, nous utilisons directement wait/notify/notifyAll pour compléter la communication inter-thread. Il n'y a pas beaucoup d'opportunités pour le modèle producteur/consommateur, car le package de concurrence Java a fourni de nombreux outils excellents et exquis, tels que divers. BlockingQueue et ainsi de suite seront présentés en détail plus tard lorsque l'occasion se présentera.

Encouragez-vous les uns les autres

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal