Se coordonner grâce aux méthodes wait et notify                  

Se coordonner grâce aux méthodes wait et notify : solution où la coordination est gérée par une ressource

     

Le problème traité ici est de type producteur-consommateur. Un ensemble de personnes dispose d'une seule boîte aux lettres, mais cette boîte présente la particularité de ne pouvoir contenir qu'une seule lettre : dès qu'elle contient une lettre, elle est considérée comme pleine. Des personnes, que l'on appelle producteurs, peuvent y déposer une lettre (si la boîte n'est pas pleine) et d'autres personnes, appelées consommateurs, peuvent y retirer une lettre (si la boîte est pleine). Les lettres ont un auteur mais pas de destinataire particulier, n'importe quel consommateur peut retirer une lettre. Chaque personne est un thread. Il y a dans notre programme deux producteurs et deux consommateurs. Chaque producteur doit déposer successivement deux lettres portant son nom et le numéro d'ordre de son courrier ; chaque consommateur doit retirer deux lettres.

Une classe BoiteAuxLettres modélise la boîte aux lettres ; deux méthodes y sont définies : les méthodes deposer et retirer. C'est la boîte aux lettres qui gère la synchronisation et la coordination de telle sorte que :

Nous donnons un aperçu d'une preuve d'absence d'interblocage pour le programme qui nous sert d'exemple. On peut d'abord vérifier que si un thread est en attente, bloqué par une instruction wait, pour déposer (resp. retirer) une lettre, c'est que la boîte est pleine (resp. vide) ; ceci est vrai car on utilise l'instruction notifyAll et ne le serait plus si on utilisait à la place l'instruction notify ; il n'y a donc pas à la fois un consommateur et un producteur qui attendent. On peut remarquer aussi que le nombre total de lettres déposées est prévu pour être égal au nombre total de lettres retirées. Si un consommateur (resp. un producteur) attend, c'est qu'il y a encore au moins une lettre qui sera déposée (resp. retirée) et que, par ailleurs, aucun producteur (resp. consommateur) n'est bloqué.

Voici l'exemple proposé.

import java.util.*;

class BoiteAuxLettres
{
  private boolean plein = false;
  private String lettre;

  synchronized void deposer(String lettre)
    {
      try
	{
	  while (plein) wait();
	}
      catch(InterruptedException exc) {}
      this.lettre=lettre;
      System.out.println("depot de : " + lettre);

      plein = true;
      notify();
    }

  synchronized String retirer(String nomConso)
    {
       try
	{
	  while (!plein) wait();
	}
       catch(InterruptedException exc) {return null;}
       System.out.println(nomConso + " lit : " + lettre);

       plein = false;
       notify();
       return lettre;
    }
}

class Producteur extends Thread
{
  BoiteAuxLettres boite;
  String nom;
  Random alea;

  Producteur(BoiteAuxLettres boite, String nom, Random alea)
    {
      this.boite = boite;
      this.nom = nom;
      this.alea = alea;
    }

  public void run()
    {
      for (int i = 0; i < ProdCons.TOTAL; i++)
	{ 
	  try
	    {
	      sleep(Math.abs(alea.nextInt())%1000);
	    }
	  catch(InterruptedException e)
	  {
	      System.out.println("interruption");
	  }
	  boite.deposer(nom + ", lettre "+ (i + 1));
	}
    }
}

class Consommateur extends Thread
{
  BoiteAuxLettres boite;
  String nom;
  Random alea;

  Consommateur(BoiteAuxLettres boite, String nom, Random alea)
    {
      this.boite=boite;
      this.nom=nom;
      this.alea=alea;
    }

  public void run()
    {
      for (int i=0;i<ProdCons.TOTAL;i++)
	{ 
	  try
	    {
	      sleep(Math.abs(alea.nextInt())%1000);
	    }
	  catch(InterruptedException e)
	  {	  
	     System.out.println("interruption");
	  }
	  boite.retirer(nom);
	}
    }
}

class ProdCons
{
  final static int TOTAL=3;

  public static void main(String[] argv)
    {
      BoiteAuxLettres boite = new BoiteAuxLettres();
      
      Random alea=new Random();

      new Producteur(boite,"David", alea).start();
      new Producteur(boite,"Antoine", alea).start();
      new Consommateur(boite,"Sophie", alea).start(); 
      new Consommateur(boite,"Marie", alea).start();     
    }
}
On a obtenu :
:    
depot de : Antoine, lettre 1
Marie lit : Antoine, lettre 1
depot de : David, lettre 1
Sophie lit : David, lettre 1
depot de : Antoine, lettre 2
Sophie lit : Antoine, lettre 2
depot de : Antoine, lettre 3
Sophie lit : Antoine, lettre 3
depot de : David, lettre 2
Marie lit : David, lettre 2
depot de : David, lettre 3
Marie lit : David, lettre 3

Vous pouvez récupérer le programme d'exemple.


© Irène Charon, Télécom ParisTech 2011