From 47fe4bec9d6469464b3a6f03ae6e9f7388aa68ea Mon Sep 17 00:00:00 2001 From: elleguehen Date: Wed, 25 Jan 2023 15:20:52 +0100 Subject: [PATCH] Fin du rendu --- rendu/rendu.md | 86 +++++- rendu/rendu_tmp.html | 665 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 744 insertions(+), 7 deletions(-) create mode 100644 rendu/rendu_tmp.html diff --git a/rendu/rendu.md b/rendu/rendu.md index 86b5d3b..dc01956 100644 --- a/rendu/rendu.md +++ b/rendu/rendu.md @@ -21,7 +21,13 @@ Ici, la `ArrayBlockingQueue` devient le moniteur, car elle gère elle-même l'ac ## 3. Fin de programme +### A. +Ca n'est pas une bonne solution, car s'il y a plus de producteurs que de consommateurs, les producteurs s'arrêtent de produire alors qu'il reste encore des consommateurs, bloquant ainsi des consommateurs qui n'ont pas acheté toutes les pâtisseries qu'ils voulaient. + +### C. +L'inconvénient est que le main doit directement interagir avec la boulangerie comme s'il était un producteur particulier. +Si on essaie d'insérer un gâteau empoisonné alors que la queue est déjà pleine, cela peut causer des soucis. ## Code @@ -33,6 +39,7 @@ public class Patisserie { } public class Gateau extends Patisserie { + public static final Gateau GATEAU_EMPOISONNE = new Gateau(); public Gateau() { } @@ -101,6 +108,29 @@ public class Patissier implements Runnable { } } +public class PatissierSuicidal extends Patissier { + public AtomicBoolean shouldRun = new AtomicBoolean(true); + + public PatissierSuicidal(Boulangerie b) { + super(b); + } + + @Override + public void run() { + while(shouldRun.get()) { + if(!local.depose(new Patisserie())){ + shouldRun.set(false); + } + System.out.println("J'ai produit ma patisserie"); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + break; + } + } + } +} + public class Boulangerie { private final ArrayList sweets = new ArrayList<>(); @@ -118,33 +148,36 @@ public class Boulangerie { } public void getStock() { - // TODO implement here + return sweets.size(); } } public class BoulangerieThreadSafe extends Boulangerie{ - private final BlockingQueue sweets = new ArrayBlockingQueue(50); + private final BlockingQueue sweets = new ArrayBlockingQueue(10); @Override - public void depose(Patisserie p) { + public boolean depose(Patisserie p) { try{ + if(sweets.remainingCapacity() <= 0) return false; sweets.put(p); - }catch(InterruptedException ignored){} + }catch(InterruptedException ignored){return false;} + return true; } @Override public Patisserie achete() { - try{return sweets.take();} catch (InterruptedException e) {} + try{return sweets.take();} catch (InterruptedException ignored) {} return null; } @Override - public void getStock() { - // TODO implement here + public int getStock() { + return sweets.size(); } } + public class ThreadWeaver { private final List runners = new ArrayList(); private final List managed = new ArrayList(); @@ -235,4 +268,43 @@ public class Main2 { tw.recover(100L); } } + +public class Main3A { + public static void main(String[] args) { + ThreadWeaver tw = new ThreadWeaver(); + Boulangerie b = new BoulangerieThreadSafe(); + Patissier p = new Patissier(b); + tw.addRunners(p, new LimitedClient(b)); + tw.weave(); + tw.run(); + + try{ + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + p.shouldRun.set(false); + tw.recover(); + } +} + +public class Main3B { + public static void main(String[] args) { + ThreadWeaver tw = new ThreadWeaver(); + Boulangerie b = new BoulangerieThreadSafe(); + Patissier p = new Patissier(b); + Client c1 = new Client(b); + tw.addRunners(p, c1); + tw.weave(); + tw.run(); + + try{ + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + b.depose(Gateau.GATEAU_EMPOISONNE); + tw.recover(); + } +} ``` \ No newline at end of file diff --git a/rendu/rendu_tmp.html b/rendu/rendu_tmp.html new file mode 100644 index 0000000..6776701 --- /dev/null +++ b/rendu/rendu_tmp.html @@ -0,0 +1,665 @@ + + + +rendu.md + + + + + + + + + + + + +

TP2 - Système

+

Elliott LE GUEHENNEC -- Yorick GEOFFRE

+

1. Simulation d'une patisserie

+

A.

+

Le patron lecteur/rédacteur présente le problème suivant : lorsqu'un lecteur lit la ressource, il la bloque et les rédacteurs ne peuvent pas la modifier. Or, dans notre configuration, il se peut que le Client doive attendre alors qu'il essaie d'obtenir sa Patisserie, mais il serait quand même en train de lire la file d'attente, empêchant ainsi le Patissier de réapprovisioner la file; c'est un deadlock.

+

B.

+

L'exclusion mutuelle se produit lorsqu'un Client veut acheter une Patisserie alors qu'aucune n'est disponible. Lorsque cela se produit, la Boulangerie bloque le thread en attendant qu'un Patissier dépose une Patisserie dans la boulangerie, débloquant ainsi la demande du client. La Boulangerie débloque le thread correspondant et retourne la Patisserie désirée au client.

+

+

La Boulangerie est donc le moniteur dans ce modèle, on l'utilise pour synchroniser toutes les opérations des Client, des Patissier, et la file d'attente.

+

Pour lancer des threads, on a créé une class ThreadWeaver. On peut lui faire passer des Runnable avec addRunners(), puis on leur assigne un thread chacun avec weave(), avant de les lancer avec run(). Avec recover(), on attend que tous les threads se soient terminés, et avec termina(), on interrompt tous les threads restants en affichant une erreur.

+

2. Simulation d'une patisserie avec collection ThreadSafe

+

Ici, la ArrayBlockingQueue devient le moniteur, car elle gère elle-même l'accès par exclusion mutuelle. Le client sera bloqué par la file, et attendra tant qu'il n'aura pas son code grâce à ArrayBlockingQueue.take();.

+

3. Fin de programme

+

A.

+

Ca n'est pas une bonne solution, car s'il y a plus de producteurs que de consommateurs, les producteurs s'arrêtent de produire alors qu'il reste encore des consommateurs, bloquant ainsi des consommateurs qui n'ont pas acheté toutes les pâtisseries qu'ils voulaient.

+

C.

+

L'inconvénient est que le main doit directement interagir avec la boulangerie comme s'il était un producteur particulier.

+

Si on essaie d'insérer un gâteau empoisonné alors que la queue est déjà pleine, cela peut causer des soucis.

+

Code

+
public class Patisserie { + + public Patisserie() { + } +} + +public class Gateau extends Patisserie { + public static final Gateau GATEAU_EMPOISONNE = new Gateau(); + + public Gateau() { + } + +} + +public class Client implements Runnable { + protected final Boulangerie local; + + public Client(Boulangerie b) { + local = b; + } + + @Override + public void run() { + while(true) { + local.achete(); + System.out.println("J'ai acheté ma patisserie"); + try { + Thread.sleep(80); + } catch (InterruptedException e) { + break; + } + } + } +} + +public class LimitedClient extends Client{ + public LimitedClient(Boulangerie b) { + super(b); + } + + @Override + public void run() { + while(true) { + local.achete(); + System.out.println("J'ai acheté ma patisserie - Limited"); + try { + Thread.sleep(80); + } catch (InterruptedException e) { + break; + } + } + } +} + +public class Patissier implements Runnable { + public AtomicBoolean shouldRun = new AtomicBoolean(true); + private final Boulangerie local; + + public Patissier(Boulangerie b) { + local = b; + } + + @Override + public void run() { + while(shouldRun.get()) { + local.depose(new Patisserie()); + System.out.println("J'ai produit ma patisserie"); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + break; + } + } + } +} + +public class PatissierSuicidal extends Patissier { + public AtomicBoolean shouldRun = new AtomicBoolean(true); + + public PatissierSuicidal(Boulangerie b) { + super(b); + } + + @Override + public void run() { + while(shouldRun.get()) { + if(!local.depose(new Patisserie())){ + shouldRun.set(false); + } + System.out.println("J'ai produit ma patisserie"); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + break; + } + } + } +} + +public class Boulangerie { + private final ArrayList<Patisserie> sweets = new ArrayList<>(); + + public synchronized void depose(Patisserie p) { + this.notify(); + sweets.add(p); + } + + public synchronized Patisserie achete() { + while(sweets.size() == 0) { + try {this.wait();} catch (InterruptedException ignored) {}} + Patisserie pat = sweets.get(0); + sweets.remove(pat); + return pat; + } + + public void getStock() { + return sweets.size(); + } + +} + +public class BoulangerieThreadSafe extends Boulangerie{ + private final BlockingQueue<Patisserie> sweets = new ArrayBlockingQueue<Patisserie>(10); + + @Override + public boolean depose(Patisserie p) { + try{ + if(sweets.remainingCapacity() <= 0) return false; + sweets.put(p); + }catch(InterruptedException ignored){return false;} + return true; + } + + @Override + public Patisserie achete() { + try{return sweets.take();} catch (InterruptedException ignored) {} + return null; + } + + @Override + public int getStock() { + return sweets.size(); + } +} + + +public class ThreadWeaver { + private final List<Runnable> runners = new ArrayList<Runnable>(); + private final List<Thread> managed = new ArrayList<Thread>(); + public void addRunners(Runnable... addedRunners){ + runners.addAll(Arrays.stream(addedRunners).toList()); + } + + public void weave(){ + for(Runnable r: runners){ + managed.add(new Thread(r)); + } + } + + public void run(){ + for(Thread t : managed){ + t.start(); + } + } + + public void recover(){ + for(Thread t : managed){ + try { + t.join(); + }catch(InterruptedException ie){ + System.out.println(ie.getMessage()); + } + } + termina(); + } + + public void recover(Long timeout){ + for(Thread t : managed){ + try { + t.join(timeout); + }catch(InterruptedException ie){ + System.out.println(ie.getMessage()); + } + } + termina(); + } + + public void termina(){ + for(Thread t : managed){ + if(t.isAlive()) { + System.out.println("Thread " + t.getName() + " has not stopped being cleaned up"); + t.interrupt(); + } + } + managed.clear(); + runners.clear(); + } +} + +public class Main { + public static void main(String[] args) { + ThreadWeaver tw = new ThreadWeaver(); + Boulangerie b = new Boulangerie(); + Patissier p = new Patissier(b); + tw.addRunners(p, new Client(b)); + tw.weave(); + tw.run(); + + try{ + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + p.shouldRun.set(false); + tw.recover(100L); + } +} + +public class Main2 { + public static void main(String[] args) { + ThreadWeaver tw = new ThreadWeaver(); + Boulangerie b = new BoulangerieThreadSafe(); + Patissier p = new Patissier(b); + tw.addRunners(p, new Client(b)); + tw.weave(); + tw.run(); + + try{ + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + p.shouldRun.set(false); + tw.recover(100L); + } +} + +public class Main3A { + public static void main(String[] args) { + ThreadWeaver tw = new ThreadWeaver(); + Boulangerie b = new BoulangerieThreadSafe(); + Patissier p = new Patissier(b); + tw.addRunners(p, new LimitedClient(b)); + tw.weave(); + tw.run(); + + try{ + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + p.shouldRun.set(false); + tw.recover(); + } +} + +public class Main3B { + public static void main(String[] args) { + ThreadWeaver tw = new ThreadWeaver(); + Boulangerie b = new BoulangerieThreadSafe(); + Patissier p = new Patissier(b); + Client c1 = new Client(b); + tw.addRunners(p, c1); + tw.weave(); + tw.run(); + + try{ + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + b.depose(Gateau.GATEAU_EMPOISONNE); + tw.recover(); + } +} +
+ + +