Conto corrente Nella famiglia Giambietti non ci sono grossi problemi economici. Infatti il padre, Enea, di professione artista free-lance, ogni tanto riesce a vendere un’opera e rimpinguare cosi` il conto. Gli altri membri della famiglia, cioe` la moglie Luisa ed il figlio Taddeo si limitano a spendere, usando il proprio bancomat, tutto quello che trovano sul conto. A proposito, il conto non puo` andare in rosso. Si implementino in Java 3 processi che simulano la famiglia Giambietti. Concorrenza 1 public class Conto { private int saldo; Conto() { saldo = 10; } public synchronized void deposita(int x) { saldo += x; } public synchronized boolean ritira(int x) { if (saldo < x) return false; else { saldo -= x; return true; } } } Concorrenza 2 public class Cons extends Thread { Conto ilSuoConto; int maxSpesa; int tempo; Cons(int m, int t, Conto c) { ilSuoConto = c; maxSpesa = m; tempo = t; } public void run () { while (true) { if(!ilSuoConto.ritira((int)(Math.random()*maxSpesa))) System.out.println("Non riesco a tirar fuori i soldi!"); else System.out.println("Riesco a tirar fuori i soldi!"); try { sleep(tempo); } catch(InterruptedException ie) { System.out.println(“ERRORE!"); } } } } Concorrenza 3 public class Marito extends Thread { private Conto ilMioConto; Marito(Conto c) { ilMioConto = c; } public void run() { while (true) { ilMioConto.deposita((int)(Math.random()*1000)); try { sleep(6000); } catch(InterruptedException ie) { System.out.println(“ERRORE!"); } } } } Concorrenza 4 public class Famiglia { public static void main(String Args[]) { Conto c = new Conto(); Marito p = new Marito(c); Cons m = new Cons(1000,4000,c); Cons f = new Cons(300,3000,c); p.start(); m.start(); f.start(); } } Concorrenza 5 Esercizio Thread Pentola • I selvaggi di una tribù mangiano servendosi da una pentola comune. • La pentola può contenere P porzioni di cibo (con P non necessariamente maggiore del numero di selvaggi). Ogni selvaggio mangia una porzione per volta. Quando la pentola si svuota (e solo allora), il cuoco provvede a riempirla con nuove P porzioni. • Tratteggiare in Java per le sole parti relative alla sincronizzazione tra i processi, i programmi che realizzano i comportamenti dei selvaggi e del cuoco e la gestione della pentola. Soluzione Class Pentola { final int P = 40; private int nPorzioni; public Pentola() { nPorzioni = P; } public syncronized void prendiPorzione(){ while ( ! (nPorzioni > 0) ) try { wait(); } catch (InterruptedException e ) { System.out.println(“ERRORE!!!!"); } nPorzioni--; System.out.println("Pentola: presa porzione"); notifyAll(); } public syncronized void riempiPentola(){ while ( ! (nPorzioni == 0) ) try { wait(); } catch (InterruptedException e ) { System.out.println(“ERRORE!!!!"); } nPorzioni = P; System.out.println("Pentola: riempita di porzioni"); notifyAll(); } } Class Cuoco extends Thread{ private Pentola pentola; public Cuoco(Pentola p) { pentola=p; } public void run (){ while(true){ pentola.riempiPentola(); System.out.println(“cuoco: ho riempito la pentola”); } } } Class Selvaggio extends Thread{ private Pentola pentola; public Selvaggio(Pentola p) { pentola=p; } public void run (){ while(true){ pentola.prendiPorzione(); System.out.println(“selvaggio:” + “ ho preso una porzione”); } } } Class Test { public static void main(String[] args) { Pentola p=new Pentola(); Cuoco c=newCuoco(p) Selvaggio s1=new Selvaggio(p); Selvaggio s2=new Selvaggio(p); Selvaggio s3=new Selvaggio(p); c.start(); s1.start(); s2.start(); s3.start(); } } Esercizio • Si consideri il sistema mostrato in figura. Il modulo PI esegue ripetutamente le seguenti operazioni: legge da tastiera una coppia di valori <i, ch>, dove i è un numero tra 0 e 3, ch un carattere, e inserisce il carattere ch nel buffer i (ognuno dei quattro buffer contiene al più un carattere). Il modulo PO considera a turno in modo circolare i quattro buffer e preleva il carattere in esso contenuto, scrivendo in uscita la coppia di valori <i, ch> se ha appena prelevato il carattere ch dal buffer i. L’accesso a ognuno dei buffer è in mutua esclusione; PI rimane bloccato se il buffer a cui accede è pieno, PO se è vuoto. 0 1 PI PO 2 3 Concorrenza 12 Parte 1 • Data la seguente sequenza di valori letta da PI, scrivere la sequenza scritta in corrispondenza da PO. <1, c> <0, b> <2, m> <0, f> <1, h> <3, n> Concorrenza 13 Soluzione 1 <0, b> <1, c> <2, m> <3, n> <0, f> <1, h> Concorrenza 14 Parte 2 • Descrivere brevemente in quali casi si può verificare una situazione di deadlock tra PI e PO. Illustrare con un semplice esempio. Concorrenza 15 Soluzione 2 • Deadlock: <1,a> <1,b> Concorrenza 16 Parte 3 • Implementare il sistema descritto in Java Concorrenza 17 Soluzione 3 //riceve l’input via linea di comando, es.”0:a 1:b 2:c” public class Buf { private char q; private boolean full; Buf() { full = false; } Concorrenza 18 public synchronized void put (char item) { while (full) try { wait(); } catch (InterruptedException e) { } q = item; full = true; notify(); } public synchronized char get () { while (!full) try { wait(); } catch (InterruptedException e) { } full = false; notify(); return q; } } Concorrenza 19 public class Pi extends Thread { private Buf[] buff; private String[] commands; Pi (Buf[] b, String[] c) { buff = b; commands = c; } public void run() { for(int i=0; i < commands.length; i++) buff[(int)commands[i].charAt(0)(int)'0'].put(commands[i].charAt(2)); } } Concorrenza 20 public class Po extends Thread { private Buf[] buff; Po (Buf[] b) { buff = b; } public void run() { while(true) { for(int i=0; i < buff.length; i++) System.out.println("Buff "+i+":"+buff[i].get()); } } } Concorrenza 21 public class pi_po { public static void main (String [] args){ Buf[] bfs = new Buf[4]; bfs[0] = new Buf(); bfs[1] = new Buf(); bfs[2] = new Buf(); bfs[3] = new Buf(); Pi pi = new Pi(bfs, args); Po po = new Po(bfs); pi.start(); po.start(); } } Concorrenza 22 Un impianto con valvola • Un impianto puo` portarsi dallo stato di funzionamento normale N in uno stato di gestione di malfunzionamento M. Entrato in tale stato, entro 5 s deve essere aperta una valvola di scarico. Se non si apre, l'impiano passa ad uno stato di fermo (F). Se la valvola viene aperta, essa rimane in tale stato per un tempo non inferiore a 20 s e non superiore a 30 s, poi l’impianto ritorna nello stato N. Concorrenza 23 Impianto in Java public class Impianto extends Thread{ private int stato; // 1 = N, -1 = M, 0 = F private Valvola valvola; public Impianto(){ stato=1; } public void run(){ while (stato != 0){ System.out.println("sto lavorando!"); while (Math.random()<.8){ // affidabilita` System.out.println("tutto bene!"); } valvola=new Valvola("Valvola Di Sfogo"); stato=-1; … valvola.start(); Concorrenza 24 … try{ System.out.println("Aspetto la valvola di sfogo!"); synchronized (valvola) { valvola.wait(5000); } } catch (InterruptedException ie){ ie.printStackTrace(); } System.out.println("Controllo che la valvola si sia aperta!"); if (valvola.getStato() == 0) { System.out.println("DISASTRO! La valvola non si e' aperta!"); stato=0; } else try{ System.out.println("La valvola si e' aperta, aspetto che finisca di sfogarsi!"); valvola.join(); }catch(InterruptedException ie){ie.printStackTrace(); }}} Concorrenza … 25 … public static void main(String [] args){ Impianto imp=new Impianto(); imp.start(); } } E ora la valvola: public class Valvola extends Thread{ private String name; private int stato; // 0 chiuso, 1 aperto public Valvola(String name){ this.name=name; this.stato = 0; } … Concorrenza 26 … public void run(){ … public int getStato() { long t = 0; return stato;} System.out.println("sono la "+name); } try{ t = (long)(5500*Math.random()); sleep(t); } catch (InterruptedException ie) {ie.printStackTrace();} System.out.println(name + ": c'ho messo " + t + "ms"); stato = 1; synchronized (this) {notify();} try{ System.out.println("Ora mi sfogo!!!"); t = (long)(20000+10000*Math.random()); sleep(t); } catch (InterruptedException ie) {ie.printStackTrace();} System.out.println(name + ": mi sono sfogata per " + t + "ms"); stato = 0;} … Concorrenza 27 Esempio Si definiscano due tipi di thread: - il tipo "a", che conta da 1 a 5, aspettando 0.2 s ad ogni passo; segnala la fine del primo conteggio; dunque riprende a contare da 5 a 1, aspettando sempre 0.2 s ad ogni passo, per poi terminare. - il tipo "b", che alla creazione si mette in attesa di un segnale da un thread, fornito alla creazione dell'oggetto, poi termina. Si construisca dunque un programma che crei e faccia partire due thread X e Y, rispettivamente di tipo "a" e di tipo "b". Y deve mettersi in attesa di X. Concorrenza 28 public class es { public static void main (String [] args){ a x = new a("X"); b y = new b("Y", (Thread)x); x.start(); y.start(); System.out.println("Fine!"); } } Concorrenza 29 public class a extends Thread{ private String name; public a(String n) { this.name = n; } public void run() { System.out.println("sono "+name); for(int i=1; i<=5; i++) { System.out.println(name + ":" + i); try { sleep(200); } catch (InterruptedException ie) { System.out.println(name+" interrotto"); } } synchronized (this) { notify();} for(int i=5; i>=1; i--) { System.out.println(name + ":" + i); try { sleep(200); } catch (InterruptedException ie) { System.out.println(name+" interrotto"); } } } } Concorrenza 30 public class b extends Thread{ String name; Thread attendo; public b(String n, Thread a) { this.name = n; this.attendo = a; } public void run() { System.out.println("sono "+name); System.out.println(name+": attendo..."); try { synchronized (attendo) { attendo.wait(); } } catch (InterruptedException ie) { System.out.println(name+" interrotto"); } System.out.println(name+": ok!"); } } Concorrenza 31 Esercizio • Due autori devono lavorare sullo stesso documento, il documento è formato da diverse parti e ogni autore può prendere il lock su al massimo 2 parti del documento e due autori non possono prendere il lock sulla stessa parte. Si descriva tale problema di concorrenza in java. Concorrenza 32 public class Section { private boolean busy; public synchronized boolean getSection(){ if (busy) { return false; } else { busy = true; return true; } } public synchronized void releaseSection() { busy = false; } } Concorrenza 33 public class Author extends Thread{ int numSections; Section Sections[10]; getSection(int n) { if(numSections<2) if(Sections[n].getSection(this)) { numSections++; } } Concorrenza 34 releaseSection(int n) { Sections[n].releaseSection(); NumSections--; } public void run () { while (true) { ……… } } } Concorrenza 35 Esercizio • Si consideri un software condiviso da 10 utenti di cui si hanno a disposizione solo 4 licenze. Ciascun utente qualora desideri utilizzare il software richiede la licenza, se essa è disponibile la acquisisce, per al più 2 ore, se non è disponibile, si mette in attesa che si liberi una licenza. • Tracciare in Java le linee principali di un programma che gestisce il problema descritto sopra. Concorrenza 36 public class ManagerLicenze { private int num_disp; public ManagerLicenze(){ num_disp = 4; } Concorrenza 37 public synchronized boolean richiediLicenza(long minuti){ if(minuti>120) return false; //ci mettiamo in attesa finchè non c'è una licenza disponibile while(num_disp==0){ try{ wait(); }catch(InterruptedException e){e.printStackTrace();} } num_disp--; return true; } public synchronized void restituisciLicenza(){ num_disp++; notify(); } } Concorrenza 38 public class Utente extends Thread{ String nome; ManagerLicenze manager; long minuti; public Utente(String nome, ManagerLicenze manager, long minuti){ this.nome = nome; this.manager = manager; this.minuti = minuti; } Concorrenza 39 public void run(){ if(manager.richiediLicenza(minuti)){ System.out.println(“L’utente "+nome+" ha preso una licenza"); try{ sleep(minuti*60000); }catch(InterruptedException e){e.printStackTrace();} manager.restituisciLicenza(); System.out.println(“L’utente " + nome + " ha lasciato la licenza dopo " + minuti + " minuti"); } else{ System.out.println("Errore: L’utente " + nome + " ha richiesto una licenza per un periodo superiore a 120 minuti."); } } } Concorrenza 40 public class Esempio{ Utente[] clienti; ManagerLicenze manager; public Esempio(int n){ clienti = new Utente[n]; manager = new ManagerLicenze(); } public static void main(String[] args){ Esempio es = new Esempio(5); es.clienti[0] = new Utente("utente1", es.manager, 30); es.clienti[1] = new Utente("utente2", es.manager, 45); es.clienti[2] = new Utente("utente3", es.manager, 120); es.clienti[3] = new Utente("utente4", es.manager, 120); es.clienti[4] = new Utente("utente5", es.manager, 120); es.clienti[0].start(); es.clienti[1].start(); es.clienti[2].start(); es.clienti[3].start(); es.clienti[4].start(); } Concorrenza } 41 Output L'utente utente1 ha acquisito una licenza. L'utente utente2 ha acquisito una licenza. L'utente utente3 ha acquisito una licenza. L'utente utente4 ha acquisito una licenza. L'utente utente1 ha rilasciato la licenza dopo 30 minuti L'utente utente5 ha acquisito una licenza. L'utente utente2 ha rilasciato la licenza dopo 45 minuti L'utente utente3 ha rilasciato la licenza dopo 120 minuti L'utente utente4 ha rilasciato la licenza dopo 120 minuti L'utente utente5 ha rilasciato la licenza dopo 120 minuti Concorrenza 42 Esercizio: sala da ballo • In una sala da ballo, m cavalieri (threads) e n dame (risorse; senza offesa) sono impegnati nel ballo liscio. Si ha m>n. Lo stato delle risorse è definito dall'array: int [n] CavaliereInCoppiaCon; dove CavaliereInCoppiaCon[j] == i se e solo se la dama j balla in coppia con il cavaliere i e CavaliereInCoppiaCon[j]== 0 se la dama j è libera. Si suppone il valore iniziale 0 per ogni dama; dopo l’inizializzazione ogni dama è sempre in coppia, salvo il transitorio durante le riassegnazioni. • Lo stato dei cavalieri è definito dall'array: int [m] stato; stato[i] assume valori: 0 (indefinito), 1 (InCoppia), 2 (InCrisi), 3 (SenzaDama) Concorrenza 43 • Prima dell’inizializzazione il valore è indefinito per ogni elemento; l’inizializzazione porta ogni cavaliere nello stato InCoppia (assegnando una dama) oppure nello stato SenzaDama (che equivale alla richiesta di ballare in coppia con una qualsiasi dama che rimanga libera). • Per ogni coppia, trascorso un certo tempo dalla formazione, il cavaliere i manifesta l’intenzione di formare una coppia diversa: il cavaliere i passa nello stato InCrisi, che equivale a dichiarare l’intenzione di rilasciare la risorsa. Il cavaliere i individua anche una dama con la quale vorrebbe ballare. La selezione è casuale ed è svolta dal metodo int damaCheAffascina(int IndiceDiCavaliere). • Le risorse sono rilasciate due per volta: se e solo se il cavaliere h che è in coppia con la dama selezionata dal cavaliere i è a sua volta InCrisi, le dame in coppia con i e h divengono libere e formano due nuove coppie. Ciascuna di esse può essere assegnata, oltre che a i o ad h, a uno qualunque dei cavalieri che si trovano nello stato SenzaDama. La selezione è eseguita in modo casuale dal metodo int ilPreferitoDi(int IndiceDiDama). A seconda che ottenga o meno l’assegnazione, ciascuno dei cavalieri i e h passa nello stato InCoppia oppure nello stato SenzaDama. • I processi che si trovano nello stato InCoppia sono in esecuzione; la transizione nello stato InCrisi o SenzaDama provoca la loro sospensione. • Realizzare il monitor SalaDaBallo per permettere ai cavalieri di ballare con le dame. public class BalloLiscio { /** Le condizioni di funzionamento del metodo main richiedono di fornire un numero di cavalieri strettamente superiore del numero delle dame ed un numero di dame strettamente superiore a 2. */ public static void main(String [] args) { int dame = Integer.parseInt(args[0]); int cavalieri = Integer.parseInt(args[1]); // aggiungere controlli nel caso le due condizioni d'ingresso // siano errate SalaDaBallo sballo= new SalaDaBallo(dame,cavalieri); System.out.println("Inizino le danze"); for (int i=1; i <= cavalieri; i++) { Cavaliere c = new Cavaliere(i,sballo); c.start(); } } } 45 import java.util.Random; public class Cavaliere extends Thread { private int indice; private SalaDaBallo sballo; // Il cavaliere conosce il suo nome e quello della sala da ballo public Cavaliere(int i, SalaDaBallo sb) { indice = i; sballo = sb; } /** Il cavaliere balla con una dama finchè non si invaghisce di un'altra. * Passerà, dunque, tra stati di ballo e sedute al tavolino.*/ public void run() { sballo.inizializzazione(indice); int damaAssegnata = sballo.laMiaDama(indice); Random gen = new Random(); while (true) { System.out.println("Il cavaliere "+indice+" balla con " + damaAssegnata); try { sleep(gen.nextInt(2000)); } catch (InterruptedException e) {} System.out.println("Il cavaliere" + indice + " e' in crisi"); sballo.coppiaInCrisi(indice); damaAssegnata = sballo.laMiaDama(indice); } } } 46