Examen 2h sur machine


L'examen est composé de trois exercices indépendants qui peuvent être traités dans l'ordre que vous voulez.


Rappel : 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érifiez 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 (ou en cas de panne).

Nous vous rappelons que la console d'Eclipse n'est pas fiable pour les programmes concurrents et qu'il est préférable d’exécuter le code dans un terminal classique.
Par exemple, pour compiler les classes d'un package nommé fr.upem.concurrence.exam et exécuter la classe Main dans un terminal, vous pouvez utiliser les commandes suivantes (en se plaçant dans le dossier qui contient les répertoires src et bin) :

    javac src/fr/upem/concurrence/exam/*.java -d bin/
    java -cp bin fr.upem.concurrence.exam.Main 

Vous avez le droit de consulter les transparents du cours.

Problèmes de concurrence

Dans cet exercice, on s'intéresse à la classe Mystery.java. Vous répondrez aux questions qui ne sont pas du code en commentaire dans le fichier Mystery.java que vous devez modifier.

public class Mystery {
    private final int[] tab = new int[4];
    private int index;

    public void store(int value) {
        index++;
        if (index >= tab.length) {
            index = 0;
        }
        tab[index] = value;
    }

    @Override
    public String toString() {
        return Arrays.toString(tab);
    }

    public static void main(String[] args) {
        var mystery = new Mystery();

        var nbThreads = 3;
        var nbRounds = 100;

        IntStream.range(0, nbThreads).forEach(j -> {
            new Thread(() -> {
                for (int i = 0; i < nbRounds; i++) {
                    mystery.store(ThreadLocalRandom.current().nextInt(10));
                    System.out.println(mystery);
                }
            }).start();
        });
    }
}

La classe Mystery n'est pas thread-safe. Pourquoi ?

Donner un scénario d'exécution dans lequel le programme lève une ArrayIndexOutOfBoundsException. Par exemple :

Exception in thread "Thread-2" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
    at fr.umlv.conc.exam2019.Mystery.store(Mystery.java:16)
    at fr.umlv.conc.exam2019.Mystery.lambda$1(Mystery.java:34)
    at java.base/java.lang.Thread.run(Thread.java:834)

Modifier le code pour rendre la classe Mystery thread-safe en utilisant l'API des ReentrantLock.

Ré-écrire le code du main pour qu'il continue à faire la même chose, mais sans démarrer les threads "à la main", c'est à dire en utilisant un ExecutorService à la place.
Il doit y avoir nbThreads qui travaillent et nbThreads * nbRounds tâches à effectuer. Chaque tâche renvoie une ligne (chaîne de caractères) à afficher et le thread main se charge de l'affichage.
Faire également en sorte que la levée d'une exception par une tâche n'arrête pas le programme (c'est mal, mais c'est juste un exercice) et que l'on affiche "oosps" à la place.
Et n'oubliez pas que votre programme doit s'arrêter.

Vente aux enchères

Dans cet exercice, on cherche à écrire une classe thread-safe Bidding qui va permettre à plusieurs threads de participer à une vente aux enchères simple : le nombre de participants est fixé à l'avance et chaque participant fait une seule enchère. Les threads devront s'inscrire avant de participer à la vente et le thread qui fait la meilleure offre remporte l'enchère. En cas d'égalité, c'est le thread qui a proposé la meilleure enchère en premier qui gagne.

Plus précisément, la classe Bidding devra fournir les 3 méthodes suivantes.

Écrire une classe Bidding thread-safe et qui respecte le contrat ci-dessus.

Rajouter à votre classe Bidding une méthode main qui crée une enchère pour 3 threads, puis démarre 5 threads. Chaque thread essaie de s'enregistrer. S'il n'y arrive pas, il affiche qu'il ne participe pas à l'enchère et s'arrête, sinon, il propose son enchère (au hasard entre 1 et 10) et affiche sa valeur et si oui ou non il a gagné. Voici un exemple d'affichage :

Thread-4 not bidding.
Thread-3 (bid = 7) lost.
Thread-1 (bid = 6) lost.
Thread-0 (bid = 9) won.
Thread-2 not bidding.

Recopier votre classe Bidding dans une classe BiddingCloseable.

On veut rajouter une méthode public void close() qui permet de fermer une enchère. Lorsque l'enchère est fermée, les threads en attente dans registerAndWait ou dans bid doivent tous lever une AsynchronousCloseException. Si des threads appellent registerAndWait ou bid, alors que l'enchère a été fermée, ces méthodes lèvent une IllegalStateException.

Modifier la classe BiddingCloseable pour qu'elle respecte ce nouveau contrat.
Modifier également le code du main pour que les threads s'arrêtent en affichant leur nom et "registration closed" si l'enchère est fermée pendant qu'ils s'enregistrent ou "bidding closed" si l'enchère est fermée pendant qu'ils enchérissent.

Pour tester, faire en sorte que l'enchère soit créée avec 8 participants (et toujours seulement 5 threads qui participent). Après avoir démarré les 5 threads, le main attend 2 secondes et appelle close(). Vous devriez obtenir un affichage ressemblant à :

Thread-1 : registration closed
Thread-4 : registration closed
Thread-2 : registration closed
Thread-0 : registration closed
Thread-3 : registration closed

Recopier votre classe BiddingCloseable dans une classe BiddingInterruptibly.

Dans cette nouvelle classe, on veut rendre les méthodes registerAndWait et bid interruptibles.
Si un thread est interrompu pendant qu'il est bloqué dans register, il est exclu de l'enchère et il lève une InterruptedException. L'enregistrement peut continuer avec d'autres threads.
Si un thread est interrompu pendant qu'il est bloqué dans bid, l'enchère est fermée (avec la méthode close()) et il lève une InterruptedException.

Modifier la classe BiddingInterruptibly pour qu'elle respecte ce nouveau contrat
Écrire un main qui créé un BiddingInterruptibly avec 5 participants. Le main démarre 4 threads qui s'enregistrent et qui enchérissent une valeur aléatoire comprise entre 1 et 10. Le cinquième thread s'enregistre et attend 10 secondes avant d'enchérir. Après avoir démarré les threads, le main interrompt l'un des quatre autres threads.

Producteur-Consommateur

Dans cet exercice, on dispose d'une API très simple pour la gestion des commandes (classe WareHouse.Order) dans un entrepôt (classe WareHouse). Elle fournit un constructeur, 2 méthodes static et une constante :

On souhaite modéliser le traitement des commandes dans l'entrepôt en respectant le principe de base du travail à la chaîne : chaque travailleur ne peut réaliser qu'une seule tâche simple et répétitive. Dans cet exercice, les travailleurs sont des threads et les tâches correspondent aux méthodes de l'API fournie.

Récupérer la classe WareHouse.java

Écrire dans la méthode main d'une classe DeliverySystem un programme qui effectue le traitement des commandes comme indiqué ci-dessous, avec la contrainte que chaque thread n'a le droit d'appeler qu'une seule des 3 méthodes de l'API (autant de fois qu'il veut, bien sûr) :
  • 1 thread se charge de récupérer en boucle des commandes en provenance de l'entrepôt en utilisant la méthode nextOrder.
  • 3 threads s'occupent de préparer les colis (parcels), c'est à dire d'obtenir leur numéro de destination à partir des commandes récupérées, en utilisant la méthode prepareParcel.
  • Pour chaque destination, un thread prépare les livraisons (delivery), c'est à dire qu'il fait des listes de 10 commandes (sans en oublier, ni en dupliquer) pour cette destination et vérifie que chaque liste est correcte en utilisant la méthode checkDelivery.

Pour tester que tout se passe bien, vous pouvez faire en sorte que chaque thread affiche ce qu'il fait. Cela donne un affichage qui peut ressembler à ça :

new order : xsuu#1
new order : igfa#1
new order : dnxn#1
missing item : dnxn#1
new order : ivfo#3
new order : aupd#0
--> ivfo#3 for dispatch to 3
new order : qpju#4
new order : kyfp#0
new order : slgb#4
new order : shbi#2
new order : nwfj#1
new order : rltt#1
new order : qlgb#2
new order : cmaj#4
new order : pdok#1
new order : rrer#3
--> igfa#1 for dispatch to 1
--> xsuu#1 for dispatch to 1
new order : nhjt#2
new order : fgik#2
missing item : qpju#4
new order : atqa#0
--> aupd#0 for dispatch to 0
new order : iupo#4
--> kyfp#0 for dispatch to 0
missing item : nwfj#1
missing item : rltt#1
--> slgb#4 for dispatch to 4
missing item : cmaj#4
new order : mien#2
new order : nace#1
new order : cbdf#0
new order : navu#2
new order : uexy#0
--> qlgb#2 for dispatch to 2
--> pdok#1 for dispatch to 1
new order : cyin#3
new order : vgsb#3
--> shbi#2 for dispatch to 2
new order : pshr#2
--> rrer#3 for dispatch to 3
new order : uxju#1
--> atqa#0 for dispatch to 0
new order : qngs#2
--> nhjt#2 for dispatch to 2
new order : nvfg#2
--> fgik#2 for dispatch to 2
new order : gpgk#4
--> iupo#4 for dispatch to 4
new order : iefp#2
--> mien#2 for dispatch to 2
new order : cmxq#3
--> navu#2 for dispatch to 2
new order : pyss#2
--> nace#1 for dispatch to 1
new order : yyjc#3
--> cyin#3 for dispatch to 3
new order : papm#1
--> vgsb#3 for dispatch to 3
new order : fmyj#2
--> cbdf#0 for dispatch to 0
missing item : uxju#1
new order : rclj#1
new order : qodr#0
missing item : qngs#2
new order : pijw#0
--> uexy#0 for dispatch to 0
new order : fsne#0
--> gpgk#4 for dispatch to 4
missing item : iefp#2
missing item : cmxq#3
missing item : pyss#2
new order : iymc#4
new order : exyf#0
new order : innw#2
new order : vvfd#1
--> yyjc#3 for dispatch to 3
--> nvfg#2 for dispatch to 2
new order : jgts#4
missing item : fmyj#2
new order : dysw#2
new order : vdke#3
--> rclj#1 for dispatch to 1
new order : lgel#3
--> qodr#0 for dispatch to 0
new order : rfmo#4
--> pshr#2 for dispatch to 2
missing item : fsne#0
new order : fiiv#4
--> iymc#4 for dispatch to 4
new order : wowb#4
new order : eihf#4
--> pijw#0 for dispatch to 0
new order : hicg#2
--> papm#1 for dispatch to 1
missing item : vvfd#1
new order : ebxb#4
new order : hcyi#2
--> jgts#4 for dispatch to 4
new order : lbcv#2
--> exyf#0 for dispatch to 0
new order : fotr#1
--> innw#2 for dispatch to 2
new order : pbxg#2
--> dysw#2 for dispatch to 2
new order : ngiy#1
delivery to 2 : [qlgb#2, shbi#2, nhjt#2, fgik#2, mien#2, navu#2, nvfg#2, pshr#2, innw#2, dysw#2]
--> vdke#3 for dispatch to 3
new order : vuwa#1
--> fiiv#4 for dispatch to 4
new order : ttkh#0
--> wowb#4 for dispatch to 4
missing item : eihf#4
new order : ftqx#4
missing item : hicg#2
new order : opqr#2
new order : ioee#1
--> rfmo#4 for dispatch to 4
new order : txoj#4
--> lgel#3 for dispatch to 3
new order : trhw#1
--> hcyi#2 for dispatch to 2
new order : hmmn#4
--> ebxb#4 for dispatch to 4
missing item : pbxg#2
new order : gaad#3
missing item : ngiy#1
new order : kxia#4
new order : keut#2
--> vuwa#1 for dispatch to 1
new order : sjnq#4
--> lbcv#2 for dispatch to 2
new order : yfdx#3
--> ftqx#4 for dispatch to 4
delivery to 4 : [slgb#4, iupo#4, gpgk#4, iymc#4, jgts#4, fiiv#4, wowb#4, rfmo#4, ebxb#4, ftqx#4]
new order : odco#2
--> fotr#1 for dispatch to 1
missing item : ioee#1
missing item : txoj#4
new order : fkyl#0
new order : urso#3
new order : atpd#1
--> ttkh#0 for dispatch to 0
new order : wwah#4
--> trhw#1 for dispatch to 1
new order : tmri#3
--> hmmn#4 for dispatch to 4
new order : vevy#4
--> opqr#2 for dispatch to 2
new order : twts#2
--> gaad#3 for dispatch to 3
new order : pgpu#2
--> keut#2 for dispatch to 2
missing item : yfdx#3
new order : vxdo#1
new order : yrob#3
--> odco#2 for dispatch to 2
new order : ssov#0
--> sjnq#4 for dispatch to 4
new order : xhne#1
--> urso#3 for dispatch to 3
missing item : atpd#1
missing item : wwah#4
new order : bceb#1
new order : xcmr#0
new order : ntjf#3
--> kxia#4 for dispatch to 4
new order : vlxs#4
--> tmri#3 for dispatch to 3
delivery to 3 : [ivfo#3, rrer#3, cyin#3, vgsb#3, yyjc#3, vdke#3, lgel#3, gaad#3, urso#3, tmri#3]
new order : nvhg#2
--> twts#2 for dispatch to 2
new order : fodh#4
--> fkyl#0 for dispatch to 0
delivery to 0 : [aupd#0, kyfp#0, atqa#0, cbdf#0, uexy#0, qodr#0, pijw#0, exyf#0, ttkh#0, fkyl#0]
new order : fgsx#4
missing item : vxdo#1
new order : vfvc#2
--> pgpu#2 for dispatch to 2
new order : boei#2
--> vevy#4 for dispatch to 4
new order : axhh#3
--> yrob#3 for dispatch to 3
new order : cbll#0
--> ssov#0 for dispatch to 0
new order : peyy#1
--> bceb#1 for dispatch to 1
delivery to 1 : [igfa#1, xsuu#1, pdok#1, nace#1, rclj#1, papm#1, vuwa#1, fotr#1, trhw#1, bceb#1]
new order : gopq#3
--> xcmr#0 for dispatch to 0
new order : abhl#1
...