Maison  >  Article  >  Java  >  Explication détaillée des instances d'attente/notification de communication entre les threads Java

Explication détaillée des instances d'attente/notification de communication entre les threads Java

怪我咯
怪我咯original
2017-06-30 10:48:541443parcourir

L'éditeur suivant vous apportera une brève discussion sur l'attente/notification de la communication entre les threads Java. L'éditeur le trouve plutôt bon, je vais donc le partager avec vous maintenant et le donner comme référence pour tout le monde. Suivons l'éditeur et jetons un coup d'œil.

Wait/notify/notifyAll en Java peut être utilisé pour implémenter la communication inter-thread. C'est une méthode de la Classe Object. sont toutes des méthodes natives. Elles dépendent de la plate-forme et sont souvent utilisées pour implémenter le modèle producteur/consommateur. 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 n'attendra qu'une notification ou une interruption. à partir d'un autre thread, après avoir appelé la méthode wait(), le verrou de l'objet sera libéré.

attendre (long) : Le délai d'attente peut atteindre de longues millisecondes. S'il n'y a pas de notification, il expirera et reviendra.

notify() : notifie un thread en attente du 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() : Avertissez tous les threads en attente sur cet objet.

Un petit exemple

Simulons un exemple simple pour illustrer Nous avons un petit restaurant de boulettes en bas et le commerce est en plein essor, là-bas. est chef et serveur dans le magasin. 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 en retire 10 portions dans une assiette.

Cette méthode présente deux gros inconvénients :

1. Si le serveur va trop assidûment à la cuisine, il sera trop fatigué. , il est préférable de servir un bol aux invités à chaque fois que vous préparez un bol, et 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 serveur ne l'a pas respecté.

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 criera "Les raviolis sont prêts et peuvent être emportés". Lorsque le serveur reçoit 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 garder les oreilles ouvertes et attendre la notification du chef.

package ConcurrentTest;

import thread.BlockQueue;

/**
 * Created by chengxiao on 2017/6/17.
 */
public class JiaoziDemo {
  //创建个共享对象做监视器用
  private static Object obj = new Object();
  //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。
  private static Integer platter = 0;
  //卖出的饺子总量,卖够100份就打烊收工
  private static Integer count = 0;

  /**
   * 厨师
   */
  static class Cook implements Runnable{
    @Override
    public void run() {
      while(count<100){
        synchronized (obj){
          while (platter<10){
            platter++;
          }
          //通知服务员饺子好了,可以端走了
          obj.notify();
          System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿");
        }
        try {
          //线程睡一会,帮助服务员线程抢到对象锁
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家");
    }
  }

  /**
   * 服务员
   */
  static class Waiter implements Runnable{
    @Override
    public void run() {
      while(count<100){
        synchronized (obj){
          //厨师做够10份了,就可以端出去了
          while(platter < 10){
            try {
              System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知...");
              obj.wait();
              BlockQueue
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          //饺子端给客人了,盘子清空
          platter-=10;
          //又卖出去10份。
          count+=10;
          System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了");
        }
      }
      System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家");

    }
  }
  public static void main(String []args){
    Thread cookThread = new Thread(new Cook(),"cookThread");
    Thread waiterThread = new Thread(new Waiter(),"waiterThread");
    cookThread.start();
    waiterThread.start();
  }
}

Résultats d'exploitation

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--打烊收工,厨师回家

Mécanisme de fonctionnement

Emprunté à "Concurrency Une image de "The Art of Programming" pour comprendre le mécanisme de fonctionnement de wait/notify

Certaines personnes pourraient être intéressées dans ce qu'on appelle la surveillance, le moniteur (monitor) et le verrouillage d'objet (lock) ne sont pas bien compris. Voici une explication simple :

jvm associe un verrou à chaque objet et classe, le verrouillage. un objet. Il s’agit d’obtenir le moniteur associé à l’objet.

Ce n'est que lorsque le verrouillage de l'objet est acquis que le moniteur peut être obtenu. Si l'acquisition du verrouillage échoue, le thread entrera dans la file d'attente de blocage si le verrouillage de l'objet est obtenu avec succès, la méthode wait() peut également être utilisée ; pour surveiller l'attente sur le serveur, le verrou sera libéré et entré dans la file d'attente.

Concernant la différence entre les serrures et les moniteurs, il existe un article très détaillé et complet que je vais citer ici. Les enfants intéressés peuvent en apprendre davantage Parlons des différences entre les serrures et les moniteurs dans. detail_Java Concurrency

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

1. le verrouillage de l'objet, puis appelle la méthode wait(), à ce moment, le thread d'attente abandonnera le verrouillage de l'objet et entrera dans la file d'attente de l'objet WaitQueue

2. , et appelle la méthode notify(), à ce moment, le thread d'attente waitThread sera déplacé de la file d'attente WaitQueue vers la file d'attente synchronisée SynchronizedQueue, et le waitThread passera de l'état d'attente à l'état bloqué. Il convient de noter que notifyThread ne libérera pas le verrou immédiatement à ce moment-là. Il continuera à s'exécuter et ne libérera le verrou qu'après avoir terminé son travail restant

3. le verrouillage d'objet de la méthode wait () revient pour continuer à effectuer les opérations suivantes ;

4. Un processus de communication inter-thread basé sur 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.

Éviter les pièges

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

1. /notify()/notifyAll(), ce qui signifie que le verrou doit être obtenu en premier. Nous l'avons déjà mentionné, car le moniteur ne peut être obtenu qu'une fois le verrou verrouillé. 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 réveillé par erreur, vous devez donc utiliser while boucleVérifiez si les conditions de réveil sont remplies avant et après l'attente pour assurer la sécurité.

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

4. le thread récupère l'objet appelant Lock.

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.

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!

Déclaration:
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