:: Enseignements :: Master :: M1 :: 2011-2012 :: Java Avancé ::
[LOGO]

Verrous et attente passive


Exercice 1 - Producteur et consommateur

On souhaite écrire un programme permettant de simuler un comportement de producteurs/consommateurs.
Un producteur est un thread qui à intervalle régulier va produire un message.
Un consommateur est un thread qui à intervalle régulier va lire un message, puis afficher ce message.

  1. Rappeler l'intérêt d'une architecture producteur/consommateur.
  2. Écrire une méthode permettant d'afficher périodiquement un message.
  3. Écrire une méthode créant le Runnable d'un producteur avec en paramètre la chaîne de caractères qui sera produite par le producteur et insérée dans une file (FIFO).
    Pour implanter la file, utiliser une implémentation de la classe java.util.Queue<E> telle java.util.LinkedList. N'oubliez pas de gérer la concurrence !
  4. Faire la même chose avec un consommateur en prenant en paramètre un entier unique id. Le consommateur, après avoir retiré un message de la file, affichera celui-ci avec son numéro unique id.
  5. On considère ci-après que les producteurs produisent deux fois plus vite que les consommateurs ne consomment. Quels sont les éventuels problèmes de votre implémentation?
    Résoudre les problèmes sans changer le type de file.
  6. On considère maintenant que la vitesse des producteurs et consommateurs n'est pas fixée et que la file possède une taille maximale. Quels nouveaux problèmes peuvent survenir?
    Peut-on y remédier de façon efficace sans changer le type de file?

Exercice 2 - File bloquante synchronisée

On souhaite maintenant écrire notre propre classe de liste bloquante appelée SynchronizedBlockingQueue, basée en interne sur une ArrayDeque.

  1. Écrire un constructeur prenant en paramètre la capacité maximale de la file bloquante.
  2. Écrire deux méthodes privées isFull et isEmpty testant si la file est pleine ou vide.
  3. Écrire les méthodes put et take qui, respectivement, insère un message dans la file en bloquant si la file est pleine, et enlève un message de la file en bloquant si la file est vide.
    Vous utiliserez la méthode Object.wait() pour effectuer l'attente. Attention aux risques de spurious wakeup (la condition doit être systématiquement vérifiée à la sortie du wait) !
    Pour réveiller les threads en attente, comparer les méthodes Object.notify() et Object.notifyAll() et indiquer celle qu'il vaut mieux utiliser ici.
  4. Comment faire pour vérifier en mode debug que les méthodes isFull et isEmpty sont bien appelées dans un contexte synchronisé ?

Exercice 3 - File bloquante verrouillée

On souhaite maintenant une autre implémentation de liste bloquante appelée LockedBlockingQueue utilisant les java.util.concurent.locks.Lock à la place des blocs synchronisés.

  1. Comment peut-on créer une condition à partir d'un Lock ?
  2. Quel est l'intérêt d'utiliser des conditions plutôt que des wait/notify ?
  3. Comment faire pour vérifier en mode debug que les méthodes isFull et isEmpty sont appelées entre les méthodes lock et unlock ?
  4. Écrire les méthodes put et take de la classe LockedBlockingQueue.