Dept. of Computer Science Lab 4: Locks, Condition Variables in Java Matteo Camilli [email protected] Laboratorio di Sistemi Operativi a.a. 2015/16 Università degli Studi di Bergamo 1 Esercizi Lab3 • Coke machine • https://bitbucket.org/sistemioperativiunibg/lab3-cokemachine 2 Lock • Un altro modo di garantire la mutua esclusione in Java è l’uso dei Lock • java.util.concurrent.locks.Lock interface 3 Lock • I vantaggi di usare un Lock rispetto ad un metodo synchronized sono: • PIU’ FLESSIBILITA’: • L’uso della keyword synchronized forza l’acquisizione/rilascio del lock in un unico blocco. Quando acquisisco lock multipli, devono essere rilasciati nell’ordine opposto in cui li ho acquisiti. • LOCK(A) > LOCK(B) > UNLOCK(B) > UNLOCK(A) • L’uso dei Lock permette di implementare facilmente un chain locking: • LOCK(A) > LOCK(B) > UNLOCK(A) > LOCK(C) > UNLOCK(B) > … • PIU’ FUNZIONALITA’: • Supporto a Condition Object multipli • L’effetto è quello di avere wait set multipli 4 Condition Variables • Conditions (chiamate anche condition variables) forniscono un meccanismo per sospendere (wait) l’esecuzione di thread fino a che non vengono notificati da altri thread che una qualche condizione risulta essere verificata. • Le condition variable sono associate ad un Lock. Non è possibile mettersi in attesa su una condition variable se non ho prima acquisito il Lock Object a cui è associata. • Quando un thread si mette in attesa su una condition variable, rilascia in modo atomico il lock e sospende l’esecuzione. 5 Condition Variables 6 Esempio • Bounded Buffer • https://bitbucket.org/sistemioperativiunibg/lab4-boundedbuffer 7 Esercizi • Coke machine: • Usando i Lock e le Condition variable del package java.util.concurrent.locks come meccanismo di sincronizzazione, si realizzi in Java una soluzione al problema di prelevare lattine di coca-cola da una macchinetta e di rifornirla nel caso in cui rimanga vuota. • Definire le classi per i thread Utente e il thread Fornitore. Completare, inoltre, lo scheletro della classe CokeMachine (riportata sotto) contenente le lattine di coca-cola (oggetti condivisi) ed i metodi: • remove(...), eseguito dal generico utente per prelevare una lattina dalla macchinetta; • deposit(...), eseguito dal fornitore del servizio per caricare la macchinetta bel caso in cui rimane vuota. • Si assuma che inizialmente la macchinetta è piena, e che un utente (a scelta: il primo a trovare la macchinetta vuota o l’utente che preleva l’ultima lattina) può segnalare al fornitore che la macchinetta è vuota. import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class CokeMachine { private static final int N = 50; //Capacità della macchinetta private int count ; //Numero effettivo di lattine presenti nella macchinetta private final Lock lock = new ReentrantLock(); //Lock per la mutua esclusione //Condition variables // .... <COMPLETARE>.... } } 8 Esercizi • Toilette: Un ristorante ha una toilette unica per uomini e donne ed ha una capacità limitata a N persone. Si assuma che ogni utente della toilette (Man / Woman) sia rappresentato in Java da un thread e che il bagno (Toilettte) sia un oggetto Java condiviso. Implementare i thread utenti della toilette e completare la definizione della classe Toilette riportata di seguito realizzando uno schema di sincronizzazione con Lock e Condition variable del package java.util.concurrent.locks che garantisca i seguenti vincoli: • nella toilette non possono esserci contemporaneamente uomini e donne • nell'accesso alla toilette, le donne hanno la priorità sugli uomini • Tali condizioni di sincronizzazione devono riflettersi nel codice della classe Toilette nel modo seguente. Il thread Woman richiamerà il metodo womanIn() per entrare nel bagno e deve essere sospeso: se ci sono uomini nel bagno, o se il bagno è pieno. Il thread Man esegue il metodo manIn() per entrare nel bagno e deve essere sospeso: se ci sono donne nel bagno, o se il bagno è pieno, o se ci sono donne in attesa, e così via come suggerito nel riquadro per i metodi per uscire dal bagno. 9 Esercizi • Toilette (scheletro): public class Toilette { private static int N; //capacità del bagno private int womenInside; // numero di donne in toilette // TODO: .. <DA COMPLETARE> public Toilette(int n){ this.N = n; // TODO: <DA COMPLETARE> } /** * Provo ad acquisire il lock sulla risorsa Toilette per uscire. * Acquisito il lock, aggiorno lo stato del bagno per indicare che ci sarà una donna in meno, * e poi prima di uscire dal bagno e rilasciare il lock: se ci sono donne che aspettano * risveglio una donna sospesa, altrimenti se non ci sono più donne risveglio tutti gli uomini. * **/ public void womanOut() { // TODO: <DA COMPLETARE> } … 10 Esercizi • Toilette (scheletro segue): … /** * Similmente alla donna, ma prima di uscire controlla: se ci sono donne in attesa di entrare * e se nel bagno non ci sono uomini, allora risveglio tutte le donne sospese; altrimenti, * se non ci sono donne che aspettano e ci sono, invece, uomini che aspettano, allora risveglio un uomo. * **/ public void manOut() { // TODO: <DA COMPLETARE> } /** * Fintanto che la toilette è piena o ci sono uomini in bagno, il thread donna si sospende * **/ public void womanIn(){ // TODO: <DA COMPLETARE> } /** Fintanto che la toilette è piena o ci sono donne in bagno o ci sono donne in attesa * di entrare, il thread uomo si sospende * **/ public void manIn() { // TODO: <DA COMPLETARE> } } 11