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.
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 }
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--打烊收工,厨师回家
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 lafile 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.
PostscriptIl 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 autresCe 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!