Verrou et attente passive
Exercice 1 - Producteur et Consommateur
On souhaite écrire un programme permettant de simuler un
comportement de producteurs/consommateurs.
Un producteur est une thread qui à interval régulier va aller stocker
dans une file bloquante un message.
Un consommateur est une thread qui a interval régulier va aller chercher
un message dans la file bloquante et afficher le message.
On entend par liste bloquante une liste qui si il n'y a plus de message
met les consommateur en attente si ceux-ci demande un message et
les reveils lorsqu'un message arrive et met les producteurs en attente
si la liste est pleine et les reveils lorsque un message est lu.
-
Nous allons utiliser un java.util.concurrent.ScheduledExecutorService pour
ordonancer dans le temps les producteurs et les consommateurs.
Rappeler la différence entre les méthodes schedule, scheduleAtFixedDelay
et scheduleAtFixedRate de ScheduledExecutorService.
Puis, créer un ScheduledExecutorService à partir
de la classe Executors et créer une tâche qui affiche bonjour
toutes les 300 millisecondes.
-
Créer une méthode créant le Runnable d'un producteur
avec en paramètre la chaine de caractère qui sera inséré par le producteur
dans la file bloquante.
Pour implanter la file bloquante, utiliser la classe
java.util.concurrent.LinkedBlockingQueue ainsi que ses méthodes
put() et take().
-
Faire la même chose avec un consommateur en prenant en paramètre un entier
unique id. Le consommateur après avoir retier le message de la file bloquante
affichera celui-ci avec son numéro uniqueid.
-
Expliquer pourquoi si on plus de consommateurs que
la taille core pool thread du scheduled executor,
il peut y avoir un deadlock.
-
Tester votre programme dans le cas où la liste bloquante est pleine
ainsi que dans le cas où la liste bloquante est vide.
Exercice 2 - File bloquante synchronizée
On souhaite maintenant écrire notre propre classe de liste bloquante
appelée SynchronizedBlockingQueue et basé en interne
sur une LinkedList.
-
Écrire un constructeur prenant en paramètre la capacité
maximal de la file bloquante.
-
Écrire deux méthodes privées isFull et
isEmpty testant si la file est pleine ou vide.
-
Écrire les méthodes put et take
qui insére un message dans la file et bloque si
la file est pleine et respectivement enlève un message de
la file et bloque si la file est vide.
Vous utiliserez la méthode Object.wait()
pour effectuer l'attente.
Attention au spurious wakeup !
Pour réveiller les threads en attente, comparer les méthodes
Object.notify() et Object.notifyAll()
et indiquer celle qu'il faut mieux utiliser ici.
-
Comment faire pour vérifier que les méthodes isFull et
isEmpty sont appelées dans un contexte synchronizé ?
Exercice 3 - File bloquante verrouillée
On souhaite maintenant une autre implantation de liste bloquante
appelée LockedBlockingQueue utilisant
les java.util.concurent.locks.Lock à la place
des blocs synchronizées.
-
Comment peut-on créer une condition à partir d'un
Lock ?
-
Quel est l'intérêt d'utiliser des conditions plutôt que
des wait/notify ?
-
Comment faire pour vérifier que les méthodes isFull et
isEmpty sont appelées entre les méthodes
lock et unlock ?
-
Écrire les méthodes put et take
de la classe LockedBlockingQueue.
Rémi Forax - Université de Marne La Vallée