Producteurs/Consommateurs

Avant-propos

Dans ce TP, on cherche à mettre en application le principe du producteur/consommateur. L'idée est de ne jamais faire de synchronisation "à la main", mais plutôt d'apprendre à utiliser judicieusement l'API java.util.concurrent.

Chaine de décodage

Dans cet exercice, on dispose d'une API simplifiée à l'extrême de décodage de messages reçus depuis internet. Elle fournit 3 méthodes static :

Récupérer la classe CodeAPI.java

Écrire dans le main d'une classe Codex un programme qui va utiliser 3 threads pour récupérer en boucle les messages codés depuis internet, 2 threads pour les décoder et 1 thread pour archiver les messages décodés. De plus, chaque thread affiche ce qu'il fait. Pour l'instant, on ignore (on ne fait rien dans ce cas) les messages dont le décodage lève une exception.

Copiez votre classe Codex dans une classe CodexWithInterruption et modifier le main pour que le programme s'arrête si l'un des messages codés produit une exception lors de son décodage.

Gestion d'un entrepôt

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
...

PokemonFactory

Dans cet exercice, on vous fournit une petite API factice qui permet d'attraper des Pokemon, de les mettre dans des Pokeball et de mettre plusieurs Pokeball dans une boîte (Crate). Le code de l'API est dans PokeAPI.java. Vous n'avez pas besoin de lire le code de l'API, toutes les informations nécessaires sont disponibles dans la brève description ci-dessous.

Un Pokemon possède un nom (name) et une rareté (rarity) comprise entre 0 et 5.

public record Pokemon(String name, int rarity){..}    

La méthode static Pokemon capture() permet d'attraper un Pokemon.

Une Pokeball possède un Pokemon pokemon et une valeur (value) entre 0 et 10 inclus.

public record Pokeball(Pokemon pokemon, int value){..}

La méthode static Pokeball trap(Pokemon pokemon) permet de mettre un Pokemon dans une Pokeball.

Une Crate contient au plus 5 Pokeball qui ont toutes la même valeur. Elle est crée grâce à la méthode static Crate box(List<Pokeball> pokeballs).

Le code ci-dessous donne un exemple d'utilisation de l'API.

 public static void main(String[] args) throws InterruptedException {
        var pokemon = PokeAPI.capture();
        System.out.println(pokemon); // Pokemon[name=Ronflex, rarity=1]
        var pokeball = PokeAPI.trap(pokemon); 
        System.out.println(pokeball); // Pokeball[pokemon=Pokemon[name=Ronflex, rarity=1], value=2]
        var crate = PokeAPI.box(List.of(pokeball));
        System.out.println(crate); // Crate[content=[Pokeball[pokemon=Pokemon[name=Ronflex, rarity=1], value=2]]]
    }    

Dans cet exercice, on vous demande dans le main d'une classe PokemonFactory de :

Écrivez la classe PokeFactory demandée.

On veut maintenant que, lorsque que l'on a réussit à produire la première Crate de Pokeball de valeur 20, le programme s'arrête. On demande que les threads qui produisent les Crate produisent une dernière Crate, même incomplète, et l'affiche avant de s'arrêter.

Copiez votre code dans une classe PokeFactoryStop et modifier le main pour obtenir le comportement demandé.