ESSI 2
Transcription
ESSI 2
ESSI 2 Programmation concurrente TD2 : sémaphores et moniteurs en Java JF Lalande, Michel Cosnard, Fabrice Peix 26 Avril 2006 inspiré des excellents TDs de Jean-Paul Rigault 1 Le coiffeur dormeur Il s’agit encore d’un de ces problèmes de synchronisation mis sous une forme “plaisante”. Mais celui-ci est encore plus sérieux que le problème des philosophes, car on en trouve une application presque directe dans certains mécanismes des systèmes d’exploitation (comme l’ordonnancement des accès disque). Un coiffeur possède un salon avec un siège de coiffeur et une salle d’attente comportant un nombre fixe F de fauteuils. S’il n’y a pas de client, le coiffeur se repose sur son siège de coiffeur. Si un client arrive et trouve le coiffeur endormi, il le réveille, s’assoit sur le siège de coiffeur et attend la fin de sa coupe de cheveux. Si le coiffeur est occupé lorsqu’un client arrive, le client s’assoit et s’endort sur une des chaises de la salle d’attente ; si la salle d’attente est pleine, le client repasse plus tard. Lorsque le coiffeur a terminé une coupe de cheveux, il fait sortir son client courant et va réveiller un des clients de la salle d’attente. Si la salle d’attente est vide, il se rendort sur son siège jusqu’à ce qu’un nouveau client arrive. Le but de de ce TD est d’associer une thread au coiffeur ainsi qu’à chaque client et de programmer une séance de coiffeur dormeur en Java... Nous allons essayer d’écrire deux versions de cet exercice, une avec les sémaphores, une avec les moniteurs. 1.1 Avec des sémaphores Pour cet exercice et un peu en avance par rapport au cours, on utilisera les sémaphores de la JDK 1.5 disponibles dans /net/opt/sun-jdk-1.5.0/. On écrira une classe pour le coiffeur (HairDresser) et une classe pour les clients (Customer). L’idée est que la communication se fasse au travers de sémaphores, bloquant l’exécution des threads quand cela est nécessaire. Solution: ../java/BarberSemaphore.java import java.util.concurrent.Semaphore; 1 // // // // // // // // ======================================================================== BarberSemaphore in Java Fabrice Peix --- ESSI --- 2006 -----------------------------------------------------------------------Usage: javac BarberSemaphore.java java BarberSemaphore nbChairs nbCustomers ======================================================================== /** * Simulate barber behaviors with Semaphore. * * @author Fabrice Peix */ public class BarberSemaphore extends Thread { /** Semaphores */ private Semaphore busyChairsC, barberChairC; public BarberSemaphore(Semaphore busyChairs, Semaphore barberChair) { this.busyChairsC = busyChairs; this.barberChairC = barberChair; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { while (true) { try { System.out.println("waiting for customer ..."); busyChairsC.acquire(); System.out.println("Customer arrive ! go to work..."); barberChairC.release(); Thread.sleep(1000); } catch (Exception e) { System.out.println("haircut interrupted"); } System.out.println("Haircut is over"); } } /** * Simulate custumer behaviors with Semaphore. * * @author Fabrice Peix */ public class CustomerSemaphore extends Thread { private Semaphore freeChairs_, busyChairs_, barberChair_; 2 public CustomerSemaphore(String name, Semaphore freeChairs, Semaphore busyChairs, Semaphore barberChair) { super(name); this.freeChairs_ = freeChairs; this.busyChairs_ = busyChairs; this.barberChair_ = barberChair; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { try { while (!freeChairs_.tryAcquire()) // Try to take a place in barbershop sleep(5000); // Go to pub drink a cup of coffee System.out.println(getName() + " Cool ! get a place"); busyChairs_.release(); System.out.println(getName() + "Waiting for barber..."); barberChair_.acquire(); freeChairs_.release(); } catch (Exception e) { e.printStackTrace(); System.out.println(getName() + "Interrupted during haircut"); } } } public static void main(String[] args) { // read command-line arguments if (args.length != 2) { System.err.println("usage: BarberSemaphore nbChairs nbCustomers"); System.exit(1); } int nbChairs = Integer.parseInt(args[0]); int nbCustomers = Integer.parseInt(args[1]); System.err.println("Starting BarberSemaphore with " + nbChairs + " chairs and " + nbCustomers + " customers"); // Initialize Semaphore Semaphore freeChairs = new Semaphore(nbChairs); Semaphore busyChairs = new Semaphore(0); Semaphore barberChair = new Semaphore(0); // Create barber thread BarberSemaphore c = new BarberSemaphore(busyChairs, barberChair); c.start(); // Create customers threads for (int i = 0; i < nbCustomers; i++) { 3 CustomerSemaphore customer = c.new CustomerSemaphore("Client " + i + ":", freeChairs, busyChairs, barberChair); customer.start(); } } } 1.2 Avec des moniteurs Pour utiliser les moniteurs, on créera une classe pour le salon de coiffeur. Cette classe Java HairDresserShop réalise sous forme d’un moniteur 1 la synchronisation de la boutique du coiffeur. Votre classe devra contenir les méthodes nécessaires aux deux threads HairDresser et Customer : – Customer utilise la méthode goToHairDresser() en lui passant son nom (ou son numéro) ; cette méthode retourne true si le client s’est effectivement fait coiffer ; – HairDresser utilise la méthode getCustomer() pour traiter le client suivant (ou attendre s’il n’y a pas de client) et la méthode showCustomerOut() pour faire sortir le client lorsque la coupe est terminée. Solution: ../java/BarberMonitor.java import java.util.Vector; //======================================================================== //BarberMonitor in Java //Fabrice Peix --- ESSI --- 2006 //-----------------------------------------------------------------------//Usage: //javac BarberMonitor.java //java BarberMonitor nbChairs nbCustomers //======================================================================== /** * Simulate the behaviors of barber in Monitor case. * * @author Fabrice Peix */ class Barber extends Thread { /** the shop the barber works at */ private BarberMonitor shop; /** Time of one cut */ private final int cutTime = 2000; /** * Create a barber in a given shop. 1 c’est-à-dire avec des notify()/notifyAll() méthodes synchronisées 4 et l’utilisation des primitives wait() et * * @param shop * The shop in which the barber is created */ Barber(BarberMonitor shop) { super("barber"); this.shop = shop; } /** * Method simulating hair cuting. */ private void hairCut() { System.out.println("Barber cutting"); try { sleep(cutTime); } catch (Exception e) { } } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { System.out.println("Barber at work "); while (true) { try { shop.getCustomer(); } catch (InterruptedException e) { e.printStackTrace(); } hairCut(); shop.showCustomerOut(); } } } /** * Simmulate Customer behavior. * * @author Fabrice Peix */ class Customer extends Thread { /** Id of this customer */ private int cust; /** The barber shop */ private BarberMonitor shop; /** * Initialize a new customer. 5 * * @param cust * the id of this new customer. * @param shop * the baber shop */ Customer(int cust, BarberMonitor shop) { super("customer " + cust); this.cust = cust; this.shop = shop; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { System.out.println("Customer " + cust + " starting"); try { while (!shop.goToHairDresser()) { System.out.println("Customer " + cust + " still needs an hair cut"); sleep(3000); } } catch (InterruptedException e1) { e1.printStackTrace(); } } } /** * Simulate the barbershop behaviors. All concurrent access are managed in this * class. */ class BarberMonitor { /** Number of chairs in waiting room */ int nbChairs; /** FIFO of customer present in the room */ private Vector<Thread> customer = new Vector<Thread>(); public BarberMonitor(int nbChairs) { this.nbChairs = nbChairs; } /** * Try to go to the barbershop. * * @return true if customer make an haircut and false otherwise. */ public boolean goToHairDresser() throws InterruptedException { Customer current = (Customer) Thread.currentThread(); 6 synchronized (current) { synchronized (this) { if (customer.size() == nbChairs) return false; System.out.println(Thread.currentThread().getName() + " waiting"); if (customer.size() == 0) notify(); customer.add(current); } current.wait(); } return true; } /** Save the last customer, used to print his name in ShowCustomerOut */ private Thread lastCustomer; /** * Make an haircut or wait for customers. */ public void getCustomer() throws InterruptedException { // wait for customer synchronized (this) { System.out.println("Barber waiting for customer"); while (customer.size() == 0) wait(); lastCustomer = customer.remove(0); synchronized (lastCustomer) { lastCustomer.notify(); } } } /** * Write to stdout the name of the last customer. * */ public synchronized void showCustomerOut() { System.out.println("Customer " + lastCustomer.getName() + " is out"); } static public void main(String[] args) { try { // read command-line arguments if (args.length != 2) { System.err.println("usage: BarberSemaphore nbChairs nbCustomers"); System.exit(1); } int nbChairs = Integer.parseInt(args[0]); int nbCustomers = Integer.parseInt(args[1]); System.out.println("Starting BarberSemaphore with " + nbChairs + " chairs and " + nbCustomers + " customers"); 7 // create shop BarberMonitor shop = new BarberMonitor(nbChairs); // create barber new Barber(shop).start(); // create customers for (int i = 0; i < nbCustomers; ++i) { new Customer(i, shop).start(); } } catch (Exception e) { // report any exceptions System.err.println("Exception in BarberShop.main" + e); } } } 1.3 Avec des Locks Pour finir on réalisera une version du coiffeur dormeur utilisant l’interface Lock (utiliser la classe ReentrantLock). On créera un certain nombre de Condition afin de réaliser la synchronisation entre les clients et le coiffeur. Solution: ../java/BarberLock.java import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //======================================================================== //BarberLock in Java //Fabrice Peix --- ESSI --- 2006 //-----------------------------------------------------------------------//Usage: //javac BarberLock.java //java BarberLock nbChairs nbCustomers //======================================================================== /** * Simulate Customer behaviors with Lock * * @author Fabrice Peix */ class CustomerLock extends Thread { static boolean notified = false; /** Waiting room capacity */ private int nbChairs; /** Le Lock used to protect shared data */ private Lock l; /** Used Condition */ private Condition customerCond, barberCond, chairCond; 8 /** * @param numero * @param l * The lock * @param customerCond * Condition used by customer to waait * @param barberCond * Condition used by barber to wait * @param barberCond * Condition used by barber to call customer on his chair * @param nbChairs * Waiting room capacity */ public CustomerLock(int numero, Lock l, Condition customerCond, Condition barberCond, Condition chairCond, int nbChairs) { super("Client " + numero); this.l = l; this.customerCond = customerCond; this.barberCond = barberCond; this.chairCond = chairCond; this.nbChairs = nbChairs; } /** * Customer go to barber * * @return true if haircut is done */ private boolean haircut() { l.lock(); try { if (BarberLock.nbFreeChairs == System.out.println("Client " return false; } if (BarberLock.nbFreeChairs == System.out.println("Client " barberCond.signal(); } BarberLock.nbFreeChairs--; while (!notified) { System.out.println("Client " customerCond.await(); } notified = false; BarberLock.nbFreeChairs++; chairCond.signal(); System.out.println("Client " + } catch (InterruptedException e) e.printStackTrace(); } finally { l.unlock(); and false otherwise 0) { + getName() + " drink coffee..."); nbChairs) { + getName() + " wake up barber"); + getName() + " wait for barber"); getName() + " is on barber chair"); { 9 } return true; } public void run() { while (!haircut()) try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * Simulate barber behavior with Lock * * @author Fabrice Peix */ class BarberLock extends Thread { /** waiting room capacity */ private int nbChairs; /** free chairs in barber waiting room */ static public int nbFreeChairs; /** The Lock */ private Lock l; /** Various Condition */ private Condition customerCond, barberCond, chairCond; /** * Initialize barber * * @param l * The lock * @param customerCond * Condition used by customer to waait * @param barberCond * Condition used by barber to wait * @param barberCond * Condition used by barber to call customer on his chair * @param nbChairs * Waiting room capacity */ public BarberLock(Lock l, Condition customerCond, Condition barberCond, Condition chairCond, int nbChairs) { super(); this.l = l; this.customerCond = customerCond; this.barberCond = barberCond; this.chairCond = chairCond; 10 this.nbChairs = nbChairs; nbFreeChairs = nbChairs; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { while (true) { l.lock(); try { while (nbFreeChairs == nbChairs) { System.out.println("Waiting for customers"); barberCond.await(); } CustomerLock.notified = true; customerCond.signal(); chairCond.await(); System.out.println("Begin hair cut"); sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { l.unlock(); } } } public static void main(String args[]) { // read command-line arguments if (args.length != 2) { System.err.println("usage: BarberSemaphore nbChairs nbCustomers"); System.exit(1); } int nbChairs = Integer.parseInt(args[0]); int nbCustomers = Integer.parseInt(args[1]); System.out.println("Starting BarberSemaphore with " + nbChairs + " chairs and " + nbCustomers + " customers"); // Create Lock and Condition Lock realLock = new ReentrantLock(); Condition customerCond = realLock.newCondition(); Condition barberCond = realLock.newCondition(); Condition chairCond = realLock.newCondition(); // Create barber thread BarberLock barber = new BarberLock(realLock, customerCond, barberCond, chairCond, nbChairs); barber.start(); 11 // Create customers threads for (int i = 0; i < nbCustomers; i++) { new CustomerLock(i, realLock, customerCond, barberCond, chairCond, nbChairs).start(); } } } 2 Bonus : et s’il s’agissait d’une chaine de coiffure ? Si vous avez eu le temps de faire travailler le coiffeur précédent à coups de moniteurs et de sémaphores, imaginons maintenant qu’il s’agisse d’une chaine de coiffure multinationale (Jean–Louis Dezange) qui souhaite passer à l’échelle en embauchant C coiffeurs. Imaginons aussi que M. Dezange soit radin sur le nombre de ciseaux disponibles X (i.e. X < C, sachant qu’une paire de ciseaux de coiffeur coûte au minimum 150 ), mais que ce n’est pas très grave car le tiers du temps, le client est en train de se faire laver les cheveux (et le shampoing coule à profusion chez M. Dezange). De toutes façons, après la coupe, il y a la phase de brushing où il n’y a pas besoin de ciseaux. Pour que le salon tourne, M. Dezange a investi dans F fauteuils de coiffure et a aussi agrandi la salle d’attente. Mais M. Dezange conserve tout de même à l’esprit qu’un beau fauteuil en cuir, ça coûte, i.e. F < C (NB : dans ce modèle, on ne compte pas les fauteuils pour laver les cheveux qui sont des banquettes où l’on peut tasser beaucoup de personnes ayant une corpulence raisonnable (D’ailleurs quand les coiffeurs dorment certains utilisent la banquette puisqu’il n’y a plus assez de fauteuils. . .)). Vous êtes directeur des ressources humaines chez M. Dezange. Vous aimeriez tester sur un modèle de simulation le nombre de coiffeurs optimal C pour utiliser au mieux vos ressources “ciseaux, fauteuils” pour répondre à la demande des clients et pour minimiser le niveau sonore des ronflements de vos coiffeurs (on supposera que ce niveau sonore n’est pas dû aux ronflements des clients, la salle d’attente étant insonorisée). Essayez d’adapter un de vos programmes (il y en a surement un pour lequel c’est plus simple) pour gérer plusieurs coiffeurs et la contrainte du nombre de ciseaux. Il est fort probable qu’il faille changer de classe pour l’acquisition des moniteurs, notamment si on suppose qu’il y a désormais F fauteuils de coiffure. Pour que la simulation soit la plus réelle possible, essayez d’introduire un temps aléatoire pour le temps du shampoing, le temps de la coupe et le brushing. 12