Examen 2h sur machine


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.

QCM (sur papier)

Paires de threads (ReentrantLock)

Les classes de cet exercice doivent être dans package fr.uge.concurrence.exo1.

Le but de cet exercice est de créer une classe thread-safe ThreadMatching qui va permettre à plusieurs threads de s'associer par paires (en récupérant un id commun et unique pour la paire).

Pour ce faire, la classe ThreadMatching possède une méthode Optional<Integer> register(String ticket). Elle prend en paramètre une chaîne de caractères ticket qui permet de faire les associations. Lorsqu'un thread (noté t1) utilise register avec un ticket inconnu, on lui affecte un identifiant id et il attend. Et si un autre thread (noté t2) utilise register avec le même ticket, il sont associés : t2 sort immédiatement de la méthode qui renvoie id et t1 est débloqué et il sortira de la méthode qui renvoie aussi id. Le ticket est alors oublié, il pourra être utilisé à nouveau par une autre paire de threads. Si un troisième thread (noté t3) arrive avec le même ticket avant que t1 soit débloqué, la méthode renvoie directement un Optional vide. Sinon, ça recommence et t3 va attendre d'être associé à un autre thread, et tous deux recevront un nouvel identifiant.

Indication 1 : pour les identifiants, il suffit d'utiliser un compteur que l'on incrémente à chaque fois qu'un nouveau ticket est proposé.

Indication 2 : à chaque ticket, il faut associer l'identifiant correspondant et de quoi savoir où on en est dans l'association (déjà 1 thread, déjà 2 threads, ...)

On rappelle qu'il ne peut y avoir aucune synchronisation dans le code du main et que les méthodes de la classe thread-safe ne doivent pas faire d'affichage.

Écrire la classe thread-safe ThreadMatching et sa méthode register(). Pour cette exercice, la gestion de la concurrence doit être faite avec des ReentrantLock.

Vous pouvez tester votre code dans une classe MainMatching avec le code suivant qui démarre 20 threads qui essaient en boucle de trouver un partenaire. Ils affichent lorsqu'ils s'inscrivent, et lorsqu'un paire est constituée, chaque thread affiche avec qui il a été associé. Normalement, le programme ne s'arrête pas.

Remarque : si vous voulez vous convaincre que les associations fonctionnent, vous pouvez tenter de supprimer le sleep ou de le réduire à quelques millisecondes).

    public static void main(String[] args) {
        var matching = new ThreadMatching();
        var nbThreads = 20;

        for (var j = 0; j < nbThreads; j++) {
            var i = j;
            Thread.ofPlatform().start(() -> {
                for (;;) {
                    try {
                        Thread.sleep(ThreadLocalRandom.current().nextInt(1500, 3000));
                        var name = Thread.currentThread().getName();
                        var ticket = "t" + (i % (nbThreads / 3));
                        System.out.println(name + " registers " + ticket);
                        var id = matching.register(ticket);
                        if (id.isEmpty()) {
                            System.out.println("NO MATCH for " + name);
                            continue;
                        }
                        System.out.println("MATCH for " + name + " (" + ticket + ") : id = " + id.orElseThrow());
                    } catch (InterruptedException e) {
                        // TODO
                    }
                }
            });
        }
    }

Dupliquer le code de ThreadMatching de la question 1 dans une classe ThreadMatchingQ2. On ajoute la constante suivante dans la classe : public static final int MAX_WAITNG = 4;
et la contrainte qu'il ne peut pas y avoir plus de MAX_WAITNG threads en train d'attendre d'être associé. S'il y a déjà le maximum de threads en attente et qu'un thread appelle register avec un nouveau ticket (c'est à dire qu'aucun thread n'attend avec ce ticket), il reçoit immédiatement un Optional vide. Sinon, il est associé comme précédemment. De plus, les associations rapportent des points au ThreadMatchingQ2 : 1 point pour une association réussie, et -5 points si un thread est arrivé alors que le nombre max est atteint. Le score n'est pas modifié si un thread reçoit un Optional vide. Ajouter une méthode int points() qui permet de consulter le total courant des points et une autre int upToPoints(int nb) qui bloque jusqu'à ce que le total des points ait dépassé nb en valeur absolue. Elle renvoie alors le total courant des points.

(BONUS) Compléter le code du main fournit pour que le nombre de points soit affiché toutes les 2 secondes et que le programme s'arrête si le total dépasse 30 en valeur absolue.

Voici un exemple d'exécution.

Thread-14 registers t4
Thread-1 registers t1
Thread-19 registers t9
Thread-3 registers t3
Thread-9 registers t9
MATCH for Thread-9 (t9) : id = 2
MATCH for Thread-19 (t9) : id = 2
Thread-13 registers t3
MATCH for Thread-13 (t3) : id = 3
MATCH for Thread-3 (t3) : id = 3
Thread-6 registers t6
Thread-16 registers t6
MATCH for Thread-16 (t6) : id = 4
MATCH for Thread-6 (t6) : id = 4
Thread-7 registers t7
Thread-5 registers t5
Thread-11 registers t1
MATCH for Thread-11 (t1) : id = 1
MATCH for Thread-1 (t1) : id = 1
Thread-15 registers t5
MATCH for Thread-15 (t5) : id = 6
MATCH for Thread-5 (t5) : id = 6
Thread-17 registers t7
MATCH for Thread-17 (t7) : id = 5
MATCH for Thread-7 (t7) : id = 5
Thread-0 registers t0
Thread-12 registers t2
Thread-10 registers t0
MATCH for Thread-10 (t0) : id = 7
MATCH for Thread-0 (t0) : id = 7
Thread-2 registers t2
MATCH for Thread-2 (t2) : id = 8
MATCH for Thread-12 (t2) : id = 8
Thread-18 registers t8
Thread-4 registers t4
MATCH for Thread-4 (t4) : id = 0
MATCH for Thread-14 (t4) : id = 0
Thread-8 registers t8
MATCH for Thread-8 (t8) : id = 9
MATCH for Thread-18 (t8) : id = 9
------------------------------------------- 10
Thread-11 registers t1
Thread-3 registers t3
Thread-13 registers t3
MATCH for Thread-13 (t3) : id = 11
MATCH for Thread-3 (t3) : id = 11
Thread-17 registers t7
Thread-1 registers t1
MATCH for Thread-1 (t1) : id = 10
MATCH for Thread-11 (t1) : id = 10
Thread-12 registers t2
Thread-19 registers t9
Thread-5 registers t5
Thread-9 registers t9
MATCH for Thread-9 (t9) : id = 14
MATCH for Thread-19 (t9) : id = 14
Thread-0 registers t0
Thread-4 registers t4
NO MATCH for Thread-4
Thread-2 registers t2
MATCH for Thread-2 (t2) : id = 13
MATCH for Thread-12 (t2) : id = 13
Thread-15 registers t5
MATCH for Thread-15 (t5) : id = 15
MATCH for Thread-5 (t5) : id = 15
Thread-16 registers t6
Thread-6 registers t6
MATCH for Thread-6 (t6) : id = 17
MATCH for Thread-16 (t6) : id = 17
Thread-7 registers t7
MATCH for Thread-7 (t7) : id = 12
MATCH for Thread-17 (t7) : id = 12
Thread-8 registers t8
Thread-10 registers t0
MATCH for Thread-10 (t0) : id = 16
MATCH for Thread-0 (t0) : id = 16
Thread-14 registers t4
Thread-18 registers t8
MATCH for Thread-18 (t8) : id = 18
MATCH for Thread-8 (t8) : id = 18
Thread-3 registers t3
------------------------------------------- 14
Thread-9 registers t9
Thread-15 registers t5
Thread-4 registers t4
MATCH for Thread-14 (t4) : id = 19
MATCH for Thread-4 (t4) : id = 19
Thread-0 registers t0
Thread-13 registers t3
MATCH for Thread-13 (t3) : id = 20
MATCH for Thread-3 (t3) : id = 20
Thread-19 registers t9
MATCH for Thread-19 (t9) : id = 21
MATCH for Thread-9 (t9) : id = 21
Thread-11 registers t1
Thread-1 registers t1
MATCH for Thread-1 (t1) : id = 24
MATCH for Thread-11 (t1) : id = 24
Thread-5 registers t5
MATCH for Thread-5 (t5) : id = 22
MATCH for Thread-15 (t5) : id = 22
Thread-2 registers t2
Thread-17 registers t7
Thread-18 registers t8
Thread-7 registers t7
MATCH for Thread-7 (t7) : id = 26
MATCH for Thread-17 (t7) : id = 26
Thread-16 registers t6
Thread-6 registers t6
MATCH for Thread-6 (t6) : id = 28
MATCH for Thread-16 (t6) : id = 28
Thread-12 registers t2
MATCH for Thread-12 (t2) : id = 25
MATCH for Thread-2 (t2) : id = 25
Thread-10 registers t0
MATCH for Thread-10 (t0) : id = 23
MATCH for Thread-0 (t0) : id = 23
Thread-8 registers t8
MATCH for Thread-8 (t8) : id = 27
MATCH for Thread-18 (t8) : id = 27
Thread-14 registers t4
Thread-4 registers t4
MATCH for Thread-4 (t4) : id = 29
MATCH for Thread-14 (t4) : id = 29
Thread-15 registers t5
Thread-13 registers t3
Thread-7 registers t7
------------------------------------------- 25
Thread-9 registers t9
Thread-1 registers t1
NO MATCH for Thread-1
Thread-19 registers t9
MATCH for Thread-19 (t9) : id = 33
MATCH for Thread-9 (t9) : id = 33
Thread-11 registers t1
Thread-6 registers t6
NO MATCH for Thread-6
Thread-3 registers t3
MATCH for Thread-3 (t3) : id = 31
MATCH for Thread-13 (t3) : id = 31
Thread-5 registers t5
MATCH for Thread-5 (t5) : id = 30
MATCH for Thread-15 (t5) : id = 30
Thread-0 registers t0
Thread-16 registers t6
Thread-8 registers t8
NO MATCH for Thread-8
Thread-17 registers t7
MATCH for Thread-17 (t7) : id = 32
MATCH for Thread-7 (t7) : id = 32
Thread-2 registers t2
Thread-12 registers t2
MATCH for Thread-12 (t2) : id = 37
MATCH for Thread-2 (t2) : id = 37
Thread-4 registers t4
Thread-10 registers t0
MATCH for Thread-10 (t0) : id = 35
MATCH for Thread-0 (t0) : id = 35
Thread-18 registers t8
Thread-9 registers t9
NO MATCH for Thread-9
Thread-14 registers t4
MATCH for Thread-14 (t4) : id = 38
MATCH for Thread-4 (t4) : id = 38
Thread-15 registers t5
Thread-19 registers t9
NO MATCH for Thread-19
Thread-17 registers t7
NO MATCH for Thread-17
Thread-8 registers t8
MATCH for Thread-8 (t8) : id = 39
MATCH for Thread-18 (t8) : id = 39
Thread-5 registers t5
MATCH for Thread-5 (t5) : id = 40
MATCH for Thread-15 (t5) : id = 40
------------------------------------------- 4
Thread-1 registers t1
MATCH for Thread-1 (t1) : id = 34
MATCH for Thread-11 (t1) : id = 34
Thread-13 registers t3
Thread-3 registers t3
MATCH for Thread-3 (t3) : id = 41
MATCH for Thread-13 (t3) : id = 41
Thread-6 registers t6
MATCH for Thread-6 (t6) : id = 36
MATCH for Thread-16 (t6) : id = 36
Thread-0 registers t0
Thread-10 registers t0
MATCH for Thread-10 (t0) : id = 42
MATCH for Thread-0 (t0) : id = 42
Thread-2 registers t2
Thread-12 registers t2
MATCH for Thread-12 (t2) : id = 43
MATCH for Thread-2 (t2) : id = 43
Thread-7 registers t7
Thread-14 registers t4
Thread-19 registers t9
Thread-4 registers t4
MATCH for Thread-4 (t4) : id = 45
MATCH for Thread-14 (t4) : id = 45
Thread-15 registers t5
Thread-9 registers t9
MATCH for Thread-9 (t9) : id = 46
MATCH for Thread-19 (t9) : id = 46
Thread-1 registers t1
Thread-13 registers t3
Thread-16 registers t6
NO MATCH for Thread-16
Thread-5 registers t5
MATCH for Thread-5 (t5) : id = 47
MATCH for Thread-15 (t5) : id = 47
Thread-12 registers t2
Thread-0 registers t0
NO MATCH for Thread-0
Thread-17 registers t7
MATCH for Thread-17 (t7) : id = 44
MATCH for Thread-7 (t7) : id = 44
Thread-3 registers t3
MATCH for Thread-3 (t3) : id = 49
MATCH for Thread-13 (t3) : id = 49
Thread-6 registers t6
Thread-8 registers t8
Thread-18 registers t8
MATCH for Thread-18 (t8) : id = 52
MATCH for Thread-8 (t8) : id = 52
Thread-11 registers t1
MATCH for Thread-11 (t1) : id = 48
MATCH for Thread-1 (t1) : id = 48
------------------------------------------- 6
Thread-2 registers t2
MATCH for Thread-2 (t2) : id = 50
MATCH for Thread-12 (t2) : id = 50
Thread-14 registers t4
Thread-10 registers t0
Thread-4 registers t4
MATCH for Thread-4 (t4) : id = 53
MATCH for Thread-14 (t4) : id = 53
Thread-15 registers t5
Thread-8 registers t8
Thread-0 registers t0
MATCH for Thread-0 (t0) : id = 54
MATCH for Thread-10 (t0) : id = 54
Thread-16 registers t6
MATCH for Thread-16 (t6) : id = 51
MATCH for Thread-6 (t6) : id = 51
Thread-9 registers t9
Thread-7 registers t7
Thread-1 registers t1
NO MATCH for Thread-1
Thread-19 registers t9
MATCH for Thread-19 (t9) : id = 57
MATCH for Thread-9 (t9) : id = 57
Thread-5 registers t5
MATCH for Thread-5 (t5) : id = 55
MATCH for Thread-15 (t5) : id = 55
Thread-18 registers t8
MATCH for Thread-18 (t8) : id = 56
MATCH for Thread-8 (t8) : id = 56
Thread-17 registers t7
MATCH for Thread-17 (t7) : id = 58
MATCH for Thread-7 (t7) : id = 58
Thread-2 registers t2
Thread-13 registers t3
Thread-3 registers t3
MATCH for Thread-3 (t3) : id = 60
MATCH for Thread-13 (t3) : id = 60
Thread-11 registers t1
Thread-4 registers t4
Thread-12 registers t2
MATCH for Thread-12 (t2) : id = 59
MATCH for Thread-2 (t2) : id = 59
Thread-16 registers t6
------------------------------------------- 11
Thread-14 registers t4
MATCH for Thread-14 (t4) : id = 62
MATCH for Thread-4 (t4) : id = 62
Thread-10 registers t0
Thread-19 registers t9
Thread-18 registers t8
NO MATCH for Thread-18
Thread-6 registers t6
MATCH for Thread-6 (t6) : id = 63
MATCH for Thread-16 (t6) : id = 63
Thread-5 registers t5
Thread-15 registers t5
MATCH for Thread-15 (t5) : id = 66
MATCH for Thread-5 (t5) : id = 66
Thread-0 registers t0
MATCH for Thread-0 (t0) : id = 64
MATCH for Thread-10 (t0) : id = 64
Thread-8 registers t8
Thread-9 registers t9
MATCH for Thread-19 (t9) : id = 65
MATCH for Thread-9 (t9) : id = 65
Thread-3 registers t3
Thread-1 registers t1
MATCH for Thread-1 (t1) : id = 61
MATCH for Thread-11 (t1) : id = 61
Thread-4 registers t4
Thread-13 registers t3
MATCH for Thread-13 (t3) : id = 68
MATCH for Thread-3 (t3) : id = 68
Thread-7 registers t7
Thread-2 registers t2
Thread-17 registers t7
MATCH for Thread-17 (t7) : id = 70
MATCH for Thread-7 (t7) : id = 70
Thread-14 registers t4
MATCH for Thread-14 (t4) : id = 69
MATCH for Thread-4 (t4) : id = 69
Thread-12 registers t2
MATCH for Thread-12 (t2) : id = 71
MATCH for Thread-2 (t2) : id = 71
Thread-16 registers t6
Thread-0 registers t0
Thread-19 registers t9
Thread-18 registers t8
MATCH for Thread-18 (t8) : id = 67
MATCH for Thread-8 (t8) : id = 67
Thread-10 registers t0
MATCH for Thread-10 (t0) : id = 73
MATCH for Thread-0 (t0) : id = 73
Thread-9 registers t9
MATCH for Thread-9 (t9) : id = 74
MATCH for Thread-19 (t9) : id = 74
Thread-6 registers t6
MATCH for Thread-6 (t6) : id = 72
MATCH for Thread-16 (t6) : id = 72
------------------------------------------- 20
Thread-15 registers t5
Thread-5 registers t5
MATCH for Thread-5 (t5) : id = 75
MATCH for Thread-15 (t5) : id = 75
Thread-17 registers t7
Thread-1 registers t1
Thread-7 registers t7
MATCH for Thread-7 (t7) : id = 76
MATCH for Thread-17 (t7) : id = 76
Thread-12 registers t2
Thread-11 registers t1
MATCH for Thread-11 (t1) : id = 77
MATCH for Thread-1 (t1) : id = 77
Thread-13 registers t3
Thread-3 registers t3
MATCH for Thread-3 (t3) : id = 79
MATCH for Thread-13 (t3) : id = 79
Thread-19 registers t9
Thread-14 registers t4
Thread-16 registers t6
Thread-0 registers t0
NO MATCH for Thread-0
Thread-2 registers t2
MATCH for Thread-2 (t2) : id = 78
MATCH for Thread-12 (t2) : id = 78
Thread-9 registers t9
MATCH for Thread-9 (t9) : id = 80
MATCH for Thread-19 (t9) : id = 80
Thread-4 registers t4
MATCH for Thread-4 (t4) : id = 81
MATCH for Thread-14 (t4) : id = 81
Thread-5 registers t5
Thread-8 registers t8
Thread-10 registers t0
Thread-18 registers t8
MATCH for Thread-18 (t8) : id = 84
MATCH for Thread-8 (t8) : id = 84
Thread-15 registers t5
MATCH for Thread-15 (t5) : id = 83
MATCH for Thread-5 (t5) : id = 83
Thread-6 registers t6
MATCH for Thread-6 (t6) : id = 82
MATCH for Thread-16 (t6) : id = 82
Thread-11 registers t1
------------------------------------------- 25
Thread-13 registers t3
Thread-3 registers t3
MATCH for Thread-3 (t3) : id = 87
MATCH for Thread-13 (t3) : id = 87
Thread-1 registers t1
MATCH for Thread-1 (t1) : id = 86
MATCH for Thread-11 (t1) : id = 86
Thread-0 registers t0
MATCH for Thread-0 (t0) : id = 85
MATCH for Thread-10 (t0) : id = 85
Thread-7 registers t7
Thread-12 registers t2
Thread-17 registers t7
MATCH for Thread-17 (t7) : id = 88
MATCH for Thread-7 (t7) : id = 88
Thread-18 registers t8
Thread-19 registers t9
Thread-15 registers t5
Thread-9 registers t9
MATCH for Thread-9 (t9) : id = 91
MATCH for Thread-19 (t9) : id = 91
FINAL SCORE : 30   

Jeu Coopératif (synchronized)

Les classes de cet exercice doivent être dans package fr.uge.concurrence.exo2.

On veut écrire un programme qui permet de constituer une équipe d'au moins MIN_TEAM_SIZE joueurs (représentés par différents threads) pour un jeu coopératif. Le programme attendra que suffisamment de threads ait demandé à faire partie de l'équipe pour démarrer le jeu. Mais comme ça peut prendre un peu de temps, les joueurs doivent avoir la possibilité de se rétracter tant que l'équipe n'est pas prête. Le tout doit se passer de façon concurrente, en utilisant une classe thread-safe CoopGame appropriée.

La classe CoopGame contiendra la constante publique suivante :

    public class CoopGame {
        public static final int MIN_TEAM_SIZE = 5;
    }

Dans le programme que vous allez écrire, 7 threads vont essayer, en boucle de s'inscrire puis de se désinscrire dans une équipe de CoopGame. Chacun attend un temps aléatoire entre 0 et 200 millisecondes entre chaque tentative (d'inscription ou de désinscription) :

Thread.sleep(ThreadLocalRandom.current().nextInt(200));
À partir du moment où l'équipe a au moins MIN_TEAM_SIZE joueurs, il est possible de se désinscrire uniquement si on ne repasse pas sous le seuil de MIN_TEAM_SIZE joueurs, sinon, le thread doit être bloqué jusqu'à ce qu'il soit possible de se désinscrire (c'est à dire qu'il y a au moins un joueur en plus).
Pendant ce temps, le main attend que l'équipe soit au complet (qu'elle ait au moins MIN_TEAM_SIZE joueurs), et lorsque c'est le cas, le main affiche la composition de l'équipe et le programme s'arrête. Idéalement, lorsque le main est débloqué, plus aucun thread ne doit pouvoir s'inscrire ou se désinscrire.

Trouvez le contrat et écrivez le code d'une classe thread-safe CoopGame qui permettra de faire communiquer tous les threads ensemble (dans cet exercice, la synchronisation devra être faite uniquement avec des blocs synchronized).
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.

t3 in
t8 in
t7 in
t5 in
t8 try out
t8 out
t4 in
t3 try out
t5 try out
t5 out
t3 out
t5 in
t2 in
t6 in
t1 in
t7 try out
Game on with team : t4, t6, t5, t7, t2, t1

Modifier votre programme pour pouvoir, en plus, afficher à intervalles de temps réguliers (toutes les 50 millisecondes), combien de threads sont dans l'équipe, combien se sont inscrits (mais sont peut-être déjà désinscrits) et combien se sont désistés depuis le début du programme. Attention les nombres doit êtres cohérents : le nombre de personnes dans l'équipe doit correspondre la différence entre les inscriptions et les désistements.

Pour vous aider, voici un exemple d'affichage.

t2 in
In team = 1, in = 1, out = 0
t5 in
t3 in
t2 try out
t2 out
In team = 2, in = 3, out = 1
t1 in
t1 try out
t1 out
t7 in
t7 try out
t7 out
t1 in
In team = 3, in = 6, out = 3
t2 in
t7 in
t1 try out
Game on with team : t7, t5, t3, t2, t1