Maison  >  Article  >  Java  >  Comment utiliser les méthodes wait() et notify() en Java pour faire fonctionner des instances de ressources partagées

Comment utiliser les méthodes wait() et notify() en Java pour faire fonctionner des instances de ressources partagées

黄舟
黄舟original
2017-10-11 09:52:411329parcourir

Cet article présente principalement Java pour utiliser la méthode wait() notify() pour exploiter les ressources partagées en détail. Il a une certaine valeur de référence. Les amis intéressés peuvent se référer à

Ressources partagées Java

1) Les méthodes wait(), notify() et notifyAll() sont des méthodes locales et sont des méthodes finales et ne peuvent pas être remplacées.

 2) L'appel de la méthode wait() d'un objet peut bloquer le thread actuel, et le thread actuel doit posséder le moniteur (c'est-à-dire le verrou ou le moniteur) de cet objet

 3) Appeler La méthode notify() d'un objet peut réveiller un thread qui attend le moniteur de cet objet. S'il y a plusieurs threads qui attendent le moniteur de cet objet, un seul d'entre eux peut être réveillé

 4) Appel La méthode notifyAll() peut réveiller tous les threads qui attendent le moniteur de cet objet

En Java, il n'y a pas de méthodes liées aux opérations PV, aux processus d'exclusion mutuelle, etc. La synchronisation des processus JAVA est réalisée via synchronisé(). Il convient de noter que la méthode synchronisée() de Java est similaire au bloc mémoire mutuellement exclusif dans le concept du système d'exploitation. Dans l'objet de classe Object en Java, il y a un pour les verrous mémoire, après. un thread acquiert le verrou mémoire, les autres threads ne peuvent pas accéder à la mémoire, réalisant ainsi des opérations simples de synchronisation et d'exclusion mutuelle en Java. Si vous comprenez ce principe, vous pouvez comprendre la différence entre synchronisé(ce) et synchronisé(statique). Fonctionnement mutuellement exclusif, et les membres statiques sont exclusifs à la classe, et leur espace mémoire est partagé par tous les membres de la classe. Cela provoque synchronisé( ) pour verrouiller les membres statiques, ce qui équivaut à verrouiller la classe, c'est-à-dire entre tous les membres de la classe. Pour implémenter l'exclusion mutuelle, un seul thread peut accéder aux instances de cette classe en même temps. Si vous avez besoin de vous réveiller entre les threads, vous devez utiliser la méthode wait() et la méthode nofity() de la classe Object.

Après avoir tant parlé, vous ne le comprendrez peut-être pas, alors utilisons un exemple pour illustrer le problème. Utilisez le multi-threading pour obtenir un 1,2,1,2,1,2,1,2 continu, Sortie 1,2.


Test :
package com.study.thread;
/**
 * 多线程
 * @ClassName: PrintFile 
 * @date 2017年10月10日 下午4:05:04
 */
public class PrintFile implements Runnable{
  //当前线程id
  private int id ;
  //共享资源
  public byte[] res ;
  
  //如果类里写了有参构造器,而任然想保留无参数构造方法,则必须显式的写出该方法。
  public PrintFile() {
    super();
//    System.out.println("我是构造器"); 
  }

  public PrintFile(int id, byte[] res) {
    //构造器中使用super()/this(),必须放在第一行。
    this(); 
    this.id = id;
    this.res = res;
  }

  //静态计数器
  public static int count = 5;
  
  @Override
  public void run() {
    synchronized (res) {
      while(count-->=0){
        try {
          res.notify();//唤醒其他线程中的某一个(唤醒等待res的其他线程,当前线程执行完后要释放锁)
          System.out.println("当前线程id值:"+id);
          
          res.wait();//当前线程阻塞,等待被唤醒
          System.out.println("现在执行的线程是"+Thread.currentThread().getName()+",--wait()后的代码继续执行:"+id);
          
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      return; 
    }
  }
}


Résultat :
package com.study.thread;

public class PrintFileTest {
 public static void main(String[] args) {
  byte[] res = new byte[]{0,1,2};//共享资源
  PrintFile p1 = new PrintFile(1, res);
  PrintFile p2 = new PrintFile(2, res);
  
  Thread t1 = new Thread(p1, "a");
  Thread t2 = new Thread(p2, "b");
    
  t1.start();
  t2.start();
 }
}

Valeur actuelle de l'identifiant du fil de discussion :1

Valeur actuelle de l'identifiant du thread : 2

Le thread en cours d'exécution est un, le code après --wait() continue de s'exécuter : 1
Valeur actuelle de l'identifiant du thread : 1
Le thread en cours d'exécution est b, le code après --wait() continue de s'exécuter : 2
Valeur actuelle de l'identifiant du thread : 2
Le thread en cours d'exécution est a, le code après --wait() continue de s'exécuter : 1
Valeur actuelle de l'identifiant du thread : 1
Le thread en cours d'exécution est b, --wait() Le code après --wait() continue de s'exécuter : 2
Valeur actuelle de l'identifiant du thread : 2
Le thread. en cours d'exécution est a,--wait() Le code après l'exécution continue : 1

Ce qui suit explique pourquoi un tel résultat se produit :

Tout d'abord, les threads 1 et 2 sont démarrés. ce thread 1 exécute d'abord la méthode run pour obtenir les ressources (en fait, c'est incertain), obtient le verrou de l'objet a et entre dans la boucle while (utilisée pour contrôler plusieurs tours de sortie) :

1 à ce moment. , l'objet appelle sa méthode de réveil notify(), ce qui signifie que ce bloc de synchronisation est exécuté. Une fois terminé, il libérera le verrou et le remettra au thread en attente de la ressource a

2; . Sortie 1 ;

3. L'objet exécute la méthode d'attente, ce qui signifie qu'il en est propriétaire à partir de ce moment. Le thread du verrou d'objet (c'est-à-dire le thread n°1 ici) libère le contrôle du CPU, libère le verrou et le thread entre dans l'état bloqué. Le code suivant n'est pas exécuté temporairement car le bloc de synchronisation n'a pas été exécuté, donc 1 n'a aucun effet

4. run, mais il n'a pas pu continuer à s'exécuter car il n'a pas obtenu le verrou de l'objet a. Cependant, après 3 étapes, il a obtenu le verrou de l'objet a, puis a exécuté le réveil de a. , de la même manière, signifie qu'une fois le bloc de synchronisation exécuté, il libérera le verrou et le remettra au thread en attente d'une ressource

5

6. . L'exécution de la méthode d'attente de a signifie que le thread qui possède le verrou d'objet à partir de ce moment (c'est-à-dire le thread n°2 ici) libère le contrôle du processeur, libère le verrou et le thread entre dans l'état de blocage. être exécuté temporairement car il n'a pas encore été exécuté.Après l'exécution du bloc de synchronisation, la méthode de réveil en 4 étapes du thread n°2 ne fonctionne pas

À ce moment, le thread n°1 ; exécute l'étape 3 et constate que le verrou d'objet n'est pas utilisé, il continue donc à exécuter l'étape 3. Dans le code après la méthode d'attente, le résultat est : ------Thread 1 acquiert le verrou et le code après l'attente () continue de s'exécuter : 1;

8 À ce moment, la boucle while remplit les conditions et continue de s'exécuter, par conséquent, exécutez à nouveau la méthode de réveil du thread n°1, ce qui signifie qu'elle est activée. libérera le verrou après l'exécution du bloc de synchronisation ;

9. Sortie 1 ;

10. Exécutez la méthode d'attente et le thread 1 est bloqué.

11. À ce moment, le thread 2 a à nouveau obtenu le verrou. Passez à l'étape 6 et continuez à exécuter le code après la méthode d'attente, le résultat est donc : ------Le thread 2 obtient le verrou, attendez. Le code après () continue de s'exécuter : 2;

12. Continuez à exécuter la boucle while et affichez

··· ··

Par ce qui précède ; étapes, je crois que tout le monde a compris l'utilisation de ces deux méthodes, mais il y a toujours un problème dans ce programme. Lorsque la boucle while ne remplit pas les conditions, il doit y avoir encore des threads en attente de ressources, donc le thread principal ne le fera jamais. mettre fin. Bien entendu, le but de ce programme est simplement de démontrer comment utiliser ces deux méthodes.

Résumé :

La méthode wait() et notify() doivent être utilisées avec synchronisé(resource). C'est-à-dire que wait et notify opèrent sur le thread qui a acquis le verrou de ressource. D'un point de vue syntaxique, Obj.wait() et Obj.notify doivent se trouver dans le bloc d'instructions synchronisé(Obj){...}. Fonctionnellement parlant, après avoir acquis le verrou d'objet, le thread wait() libère activement le contrôle du processeur et le verrou d'objet, et en même temps, ce thread se met en veille. Jusqu'à ce qu'un autre thread appelle notify() de l'objet pour réveiller le thread, il peut continuer à acquérir le verrou d'objet et poursuivre l'exécution. Le notify() correspondant est l'opération de libération du verrou d'objet. [Par conséquent, nous pouvons constater que les méthodes wait et notify peuvent libérer le verrou de l'objet, mais wait libère également le contrôle du processeur, c'est-à-dire que le code derrière lui cesse de s'exécuter et que le thread entre dans un état bloqué, tandis que la méthode notify ne libère pas Contrôle du processeur immédiatement, mais le verrou est automatiquement libéré après la fin de l'exécution du bloc d'instructions synchronisé(){} correspondant. 】Après avoir libéré le verrou, la JVM sélectionnera un thread parmi les threads en attente de ressource, lui accordera le verrou d'objet, réveillera le thread et poursuivra l'exécution. Cela fournit des opérations de synchronisation et de réveil entre les threads. Thread.sleep() et Object.wait() peuvent suspendre le thread actuel et libérer le contrôle du processeur. La principale différence est que Object.wait() libère le contrôle de verrouillage d'objet tout en libérant le processeur, alors qu'il est dans le bloc de synchronisation The Thread. La méthode sleep() ne libère pas le verrou, libère uniquement le contrôle du processeur.

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