L'examen est composé d'une partie écrite sur feuille de 40 minutes maximum, à faire en premier et de deux exercices qui peuvent être traités dans l'ordre que vous voulez.
Rappel : si ce n'est pas déjà le cas, vous devez configurer le workspace d'Eclipse (File > Switch WorkSpace) pour qu'il corresponde au répertoire EXAM présent dans le home de votre session de TP noté.
Vérifier que tous vos fichiers sont bien dans le répertoire EXAM. Tout ce qui n'est pas dans ce répertoire est perdu quand vous vous déconnectez.
La javadoc 23 est là : https://igm.univ-mlv.fr/~juge/javadoc-23/.
Vous avez le droit de consulter les transparents du cours pendant tout l'examen.
synchronized
)Les classes de cet exercice doivent être dans package fr.uge.concurrence.exo1.
Dans cet exercice, on cherche à créer une classe thread-safe ThreadStopper
qui va permettre à plusieurs threads de se synchroniser. La classe ThreadStopper
est construite pour un certain nombre threshold
de threads. La classe ThreadStopper
possède une méthode participate()
. Tant que cette méthode n'a pas été appelée par threshold
threads, les threads qui l'appellent restent bloqués. Lorsque threshold
threads l'ont appelée, tous les threads sont débloqués. Si plus de threshold
threads appellent la méthode particpate
, la méthode lève une exception IllegalStateException
.
Écrire la classe thread-safe ThreadStopper
et sa méthode participate()
. Pour cet exercice, la gestion de la concurrence doit être faite avec des blocs synchronized
.
Vous pouvez tester votre code dans une classe Main
avec le code suivant.
public static void main(String[] args) throws InterruptedException { var nbThreads = 10; var threadStopper = new ThreadStopper(nbThreads); LongStream.range(0, nbThreads).forEach(id -> { Thread.ofPlatform().start(() -> { try { Thread.sleep(id * 1_000); System.out.println("Thread " + id + " starts participating and is blocked"); threadStopper.participate(); System.out.println("Thread " + id + " has finished and is unblocked"); } catch (InterruptedException e) { throw new AssertionError(); } }); }); }Vous devriez avoir un affichage similaire à ceci :
Thread 0 starts participating and is blocked Thread 1 starts participating and is blocked Thread 2 starts participating and is blocked Thread 3 starts participating and is blocked Thread 4 starts participating and is blocked Thread 5 starts participating and is blocked Thread 6 starts participating and is blocked Thread 7 starts participating and is blocked Thread 8 starts participating and is blocked Thread 9 starts participating and is blocked Thread 9 has finished and is unblocked Thread 3 has finished and is unblocked Thread 5 has finished and is unblocked Thread 0 has finished and is unblocked Thread 6 has finished and is unblocked Thread 7 has finished and is unblocked Thread 8 has finished and is unblocked Thread 4 has finished and is unblocked Thread 2 has finished and is unblocked Thread 1 has finished and is unblocked
On veut maintenant ajouter une méthode Set<Thread> waiting()
à la classe ThreadStopper
qui renvoie l'ensemble des objets Thread
qui sont bloqués.
Dupliquer le code de ThreadStopper
de la question 1 dans une classe ThreadStopperQ2
et rajouter la méthode
waiting
.
Vous pouvez tester avec le code suivant.
public static void main(String[] args) throws InterruptedException { var nbThreads = 10; var threadStopper = new ThreadStopperQ2(nbThreads); LongStream.range(0, nbThreads).forEach(id -> { Thread.ofPlatform().start(() -> { try { Thread.sleep(id * 1_000); System.out.println("Thread " + id + " starts participating and is blocked"); threadStopper.participate(); System.out.println("Thread " + id + " has finished and is unblocked"); } catch (InterruptedException e) { throw new AssertionError(); } }); }); Thread.sleep(2_000); var waiting = threadStopper.waiting(); while (!waiting.isEmpty()) { Thread.sleep(500); System.out.println("Threads waiting : " + threadStopper.waiting().stream().map(Thread::getName).toList()); waiting = threadStopper.waiting(); } }Vous devriez avoir un affichage similaire à ceci :
Thread 0 starts participating and is blocked Thread 1 starts participating and is blocked Thread 2 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-0] Thread 3 starts participating and is blocked Threads waiting : [Thread-2, Thread-3, Thread-0, Thread-1] Threads waiting : [Thread-2, Thread-3, Thread-0, Thread-1] Thread 4 starts participating and is blocked Threads waiting : [Thread-2, Thread-0, Thread-4, Thread-3, Thread-1] Threads waiting : [Thread-2, Thread-0, Thread-4, Thread-3, Thread-1] Thread 5 starts participating and is blocked Threads waiting : [Thread-4, Thread-2, Thread-5, Thread-3, Thread-0, Thread-1] Threads waiting : [Thread-4, Thread-2, Thread-5, Thread-3, Thread-0, Thread-1] Thread 6 starts participating and is blocked Threads waiting : [Thread-3, Thread-2, Thread-6, Thread-5, Thread-1, Thread-4, Thread-0] Threads waiting : [Thread-3, Thread-2, Thread-6, Thread-5, Thread-1, Thread-4, Thread-0] Thread 7 starts participating and is blocked Threads waiting : [Thread-7, Thread-2, Thread-3, Thread-1, Thread-5, Thread-6, Thread-0, Thread-4] Threads waiting : [Thread-7, Thread-2, Thread-3, Thread-1, Thread-5, Thread-6, Thread-0, Thread-4] Thread 8 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-4, Thread-0, Thread-8, Thread-5, Thread-7, Thread-3, Thread-6] Threads waiting : [Thread-2, Thread-1, Thread-4, Thread-0, Thread-8, Thread-5, Thread-7, Thread-3, Thread-6] Thread 9 starts participating and is blocked Thread 9 has finished and is unblocked Thread 5 has finished and is unblocked Thread 2 has finished and is unblocked Thread 8 has finished and is unblocked Thread 0 has finished and is unblocked Thread 7 has finished and is unblocked Thread 6 has finished and is unblocked Thread 3 has finished and is unblocked Thread 4 has finished and is unblocked Thread 1 has finished and is unblocked Threads waiting : []
On veut maintenant ajouter une méthode void release(Thread thread)
à la classe ThreadStopperQ2
qui permet de relâcher le Thread
passé en paramètre s'il est bloqué dans la méthode participate()
.
★ Dupliquer le code de ThreadStopperQ2
de la question 2 dans une classe ThreadStopperQ3
et rajouter la méthode
release
. Par ailleurs, la méthode participate()
doit désormais renvoyer un booléen indiquant si le thread a été débloqué par un autre appel à participate()
et dans ce cas la méthode renvoie true
, ou par un appel à release()
et dans ce cas la méthode renvoie false
.
Vous pouvez tester votre code avec le main
suivant :
public static void main(String[] args) throws InterruptedException { var nbThreads = 11; var threadStopper = new ThreadStopperQ3(nbThreads-1); var threads = new Thread[nbThreads]; IntStream.range(0, nbThreads).forEach(id -> { threads[id] = Thread.ofPlatform().start(() -> { try { Thread.sleep(id * 1_000); System.out.println("Thread " + id + " starts participating and is blocked"); var finished = threadStopper.participate(); if (finished) { System.out.println("Thread " + id + " has finished and is unblocked"); return; } System.out.println("Thread " + id + " was released"); } catch (InterruptedException e) { throw new AssertionError(); } }); }); Thread.sleep(2_000); var waiting = threadStopper.waiting(); while (!waiting.isEmpty()) { threadStopper.release(threads[5]); Thread.sleep(500); System.out.println("Threads waiting : " + threadStopper.waiting().stream().map(Thread::getName).toList()); waiting = threadStopper.waiting(); } }
Et vous pourriez obtenir un affichage qui ressemble à ceci :
Thread 0 starts participating and is blocked Thread 1 starts participating and is blocked Thread 2 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-0] Thread 3 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0] Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0] Thread 4 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0, Thread-4] Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0, Thread-4] Thread 5 starts participating and is blocked Threads waiting : [Thread-2, Thread-5, Thread-1, Thread-3, Thread-0, Thread-4] Thread 5 was released Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0, Thread-4] Thread 6 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0, Thread-4, Thread-6] Threads waiting : [Thread-2, Thread-1, Thread-3, Thread-0, Thread-4, Thread-6] Thread 7 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-7, Thread-3, Thread-0, Thread-4, Thread-6] Threads waiting : [Thread-2, Thread-1, Thread-7, Thread-3, Thread-0, Thread-4, Thread-6] Thread 8 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-7, Thread-8, Thread-3, Thread-0, Thread-4, Thread-6] Threads waiting : [Thread-2, Thread-1, Thread-7, Thread-8, Thread-3, Thread-0, Thread-4, Thread-6] Thread 9 starts participating and is blocked Threads waiting : [Thread-2, Thread-1, Thread-7, Thread-8, Thread-3, Thread-0, Thread-4, Thread-6, Thread-9] Threads waiting : [Thread-2, Thread-1, Thread-7, Thread-8, Thread-3, Thread-0, Thread-4, Thread-6, Thread-9] Thread 10 starts participating and is blocked Thread 10 has finished and is unblocked Thread 9 has finished and is unblocked Thread 0 has finished and is unblocked Thread 7 has finished and is unblocked Thread 4 has finished and is unblocked Thread 8 has finished and is unblocked Thread 2 has finished and is unblocked Thread 1 has finished and is unblocked Thread 6 has finished and is unblocked Thread 3 has finished and is unblocked Threads waiting : []
ReentrantLock
)Les classes de cet exercice doivent être dans le package fr.uge.concurrence.exo2.
On veut écrire un programme qui permet à des parents de mettre des pièces dans une tirelire et à leurs enfants d'en prendre. Les enfants s'arrêtent de prendre des pièces quand ils ont économisé une somme supérieure ou égale à TARGET_VALUE
. Le tout doit se passer de façon concurrente, en utilisant une classe thread-safe PiggyBank
appropriée.
La classe PiggyBank
contiendra les constantes publiques suivantes :
public class PiggyBank { public static final int MIN_COINS = 5; public static final int MAX_COIN_VALUE = 4; public static final int MAX_SUM = 30; public static final int TARGET_VALUE = 20; }
Il y a MAX_COIN_VALUE
types de pièce de monnaie différents qui valent respectivement 1, 2, ..., MAX_COIN_VALUE
. La valeur totale des pièces contenues dans la tirelire ne doit jamais dépasser MAX_SUM
.
Plus précisément, 2 threads jouent le rôle de parents. Ils vont essayer, en boucle, de mettre des pièces dont la valeur est tirée au hasard dans la tirelire, tant que ça ne fait pas dépasser la valeur totale de la tirelire du maximum MAX_SUM
. Si le maximum est atteint, les parents attendent que ce soit de nouveau possible avant d'ajouter une pièce. Pendant ce temps-là, 3 threads qui jouent le rôle des enfants essaient, en boucle, de prendre des pièces dans la tirelire, s'il y en a. Sinon, ils attendent jusqu'à ce que ce soit possible.
Par défaut les enfants prennent une seule pièce (celle qui a été mise depuis le plus longtemps dans la tirelire). Mais si, par chance, il y a au moins MIN_COINS = 5
pièces dans la tirelire, ils en prennent exactement MIN_COINS
d'un coup. Ce sont toujours les pièces qui ont été ajoutées depuis le plus longtemps qui sont retirées en premier. Lorsqu'un enfant a accumulé des pièces pour une valeur d'au moins TARGET_VALUE
, son thread s'arrête. Quand tous les enfants ont assez d'argent, le programme s'arrête.
Chacun des threads affiche les actions qu'il réalise au fur et à mesure.
La valeur des pièces doit être tirée au hasard entre 1 et MAX_COIN_VALUE
. Pour ça, vous pouvez écrire :
var coin = ThreadLocalRandom.current().nextInt(MAX_COIN_VALUE) + 1;
Trouvez le contrat et écrivez le code d'une classe thread-safe PiggyBank
qui permettra de faire communiquer tous les threads ensemble (pour cet exercice, la synchronisation devra être faite uniquement avec des ReentrantLock
).
Dans le main
d'une classe Application
écrivez le code réalisant le programme décrit plus haut.
On rappelle qu'il ne peut y avoir aucune synchronisation (y compris pas de join
) dans le code du main
et que les méthodes de la classe thread-safe ne doivent pas faire d'affichage.
Pour vous aider, voici un exemple d'affichage que l'on peut obtenir. Ici, on fait attendre chaque parent entre 0 et 1 seconde avec Thread.sleep(ThreadLocalRandom.current().nextInt(1000))
avant d'effectuer chaque action et on fait attendre chaque enfant entre 0 et 2 secondes.
parent 2 gives a coin of value 1 parent 2 gives a coin of value 2 parent 1 gives a coin of value 4 child 3 collects 1 coin for value 1 parent 2 gives a coin of value 2 child 2 collects 1 coin for value 2 parent 2 gives a coin of value 2 parent 1 gives a coin of value 3 parent 2 gives a coin of value 2 child 1 collects 5 coins for value 13 parent 1 gives a coin of value 1 parent 1 gives a coin of value 3 parent 1 gives a coin of value 2 child 3 collects 1 coin for value 1 parent 2 gives a coin of value 1 child 2 collects 1 coin for value 3 parent 1 gives a coin of value 1 child 3 collects 1 coin for value 2 child 1 collects 1 coin for value 1 child 3 collects 1 coin for value 1 parent 2 gives a coin of value 2 parent 1 gives a coin of value 1 child 3 collects 1 coin for value 2 parent 1 gives a coin of value 1 parent 2 gives a coin of value 2 parent 2 gives a coin of value 2 child 2 collects 1 coin for value 1 parent 2 gives a coin of value 4 parent 1 gives a coin of value 4 child 3 collects 5 coins for value 13 child 3 has enough with value 20 and stops parent 1 gives a coin of value 2 child 1 collects 1 coin for value 2 parent 2 gives a coin of value 2 child 2 collects 1 coin for value 2 parent 1 gives a coin of value 1 child 1 collects 1 coin for value 1 parent 2 gives a coin of value 3 child 2 collects 1 coin for value 3 parent 2 gives a coin of value 1 parent 2 gives a coin of value 1 parent 2 gives a coin of value 2 child 2 collects 1 coin for value 1 parent 2 gives a coin of value 4 child 1 collects 1 coin for value 1 parent 2 gives a coin of value 1 parent 2 gives a coin of value 3 parent 1 gives a coin of value 1 parent 1 gives a coin of value 1 parent 2 gives a coin of value 3 child 2 collects 5 coins for value 11 child 2 has enough with value 23 and stops parent 1 gives a coin of value 4 parent 1 gives a coin of value 2 parent 1 gives a coin of value 4 parent 1 gives a coin of value 2 parent 2 gives a coin of value 1 child 1 collects 5 coins for value 14 child 1 has enough with value 32 and stops