Appunti di Sistemi Operativi Enzo Mumolo e-mail address :[email protected] web address :www.units.it/mumolo Indice 1 Esempi di programmazione concorrente 1.1 Scrittura di un grafo alle precedenze co un linguaggio concorrente . . . . . . . . 1.2 Una soluzione ibrida al problema del Produttore/Consumatore . . . . . . . . . 1.3 Realizzazione in Java del calcolo concorrente . . . . . . . . . . . . . . . . . . . . 1.4 Mutua esclusione con la primitiva 'scambia' . . . . . . . . . . . . . . . . . . . . 1.5 Metodo synchronized per la mutua esclusione . . . . . . . . . . . . . . . . . . . 1.6 Un semplice problema di sincronizzazione . . . . . . . . . . . . . . . . . . . . . 1.7 Una soluzione semaforica al problema del produttore/consumatore . . . . . . . 1.8 Una soluzione semaforica al problema dei lettori/scrittori . . . . . . . . . . . . . 1.9 Somma concorrente degli elementi di un array . . . . . . . . . . . . . . . . . . . 1.10 Applicazione del problema dei lettori/scrittori alla sincronizzazione di un ponte 1.11 Il prolema dei 5 loso mangiatori di spaghetti . . . . . . . . . . . . . . . . . . i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 3 3 7 8 9 10 12 14 16 18 Capitolo 1 Esempi di programmazione concorrente In questo capitolo vengono descritti ulteriori problemi di programmazione concorrente. 1.1 Scrittura di un grafo alle precedenze co un linguaggio concorrente Si consideri la seguente procedura in C: float calcola(float a, float b, float c, float d, float e, float f) { float x, y, z, t; x=a*sqrt(b) ; y=c*c*d; z=y+e ; t=y*f ; z =x*z ; t=x*t*t ; return(z*t) ; } Si supponga di riscrivere la medesima procedura usando un linguaggio di programmazione concorrente. Precisamente, si riscriva la procedura usando le primitive • (p=fork(processo), join(p)) • (cobegin, coend). Si considerino i seguenti sei thread, espressi in pseudo-codice: • T1(a,b) x=a*sqrt(b) ; return x ; • T2(c,d) y=c*c*d; return y; T3(y,f) t=y*f ; return t ; • T4(y,e) z=y+e ; return z ; T5(x,t) t=x*t*t ; return t ; • T6(x,z) z=x*z ; return z ; Con queste denizioni, la funzione in ambiente concorrente, è espressa con il seguente grafo : Usando un linguaggio di programmazione concorrente che realizza la concorrenza con le istruzioni cobegin/coend, il grafo corrisponde al seguente programma concorrente: float calcola(a,b,c,d,e,f) { cobegin T1; T2; coend; 1 1.1 Scrittura di un grafo alle precedenze co un linguaggio concorrente B A E D C F T1 2 T2 T3 T4 T5 T6 + Figura 1.1 cobegin coend; cobegin } Grafo alle precedenze che rappresenta la funzione T3; T4; T5; T6; coend; return(z+t); Usando invece un linguaggio che realizza la concorrenza con le primitive fork/join, si può realizzare il seguente programma concorrente: float calcola(a,b,c,d,e,f) { P1=fork(T1); T2; P2=fork(T4); T3; Join(P1); T5; Join(P2); T6; 1.2 Una soluzione ibrida al problema del Produttore/Consumatore } 3 Return(z+t): 1.2 Una soluzione ibrida al problema del Produttore/Consumatore Per soluzione ibrida si intende qui utilizzare l'algoritmo di Peterson per la mutua esclusione e due semafori per le sincronizzazioni. Lo pseudocodice di tale algoritmo é quindi il seguente: produttore(){ while(true){ p=0; t=1; elto=produci(); down(pieno); while(q!=1 && t==1) ; *ptr++=elto ; p=1 ; up(vuoto) ; } } consumatore(){ while(true){ q=0; t=2; down(vuoto); while(p!=1 && t==2) ; elto=*ptr-- ; q=1 ; up(pieno) ; } } Questo algoritmo usa un buer condiviso (puntato da ptr, puntatore condiviso), due semafori per la sincronizzazione del buer, pieno e vuoto, e tre variabili condivise, p, q e t per l'algoritmo di Peterson. 1.3 Realizzazione in Java del calcolo concorrente In questo esempio si usa il calcolo concorrente per valutare un polinomio in un punto x. /* principale.java */ /************************************************ Il grafo delle precedenze si puo' rappresentare nel modo seguente P1 \ P2 \ \ \ \ \ \ P3 \ \ / P4 / / / / I thread valutano l'espressione 3*a^3+2*b^2+3*d^3 in modo concorrente. P1 -> x=3*a^3 P2 -> y=2*b^3 P3 -> z=3*d^3 P4 -> t=y*z P5 -> k=x+t \ / P5 ***********************************************/ import java.lang.*; import java.util.*; public class principale { public static void main(String[] args) { //////////////////////////////// String[] str= new String[3]; 1.3 Realizzazione in Java del calcolo concorrente str[0]="2"; str[1]="3"; str[2]="4"; /////////////////////////////// double a,b,d; a=Double.parseDouble(str[0]); b=Double.parseDouble(str[1]); d=Double.parseDouble(str[2]); data item= new data(a,b,d); P1 P2 P3 P4 P5 p1=new p2=new p3=new p4=new p5=new P1(item); P2(item); P3(item); P4(item); P5(item); // istanze p1.start(); // calcolo p2.start(); p3.start(); try { p2.join(); } catch(InterruptedException e) try { p3.join(); } catch(InterruptedException e) p4.start(); try { p1.join(); } catch(InterruptedException e) try { p4.join(); } catch(InterruptedException e) p5.start(); try { p5.join(); } catch(InterruptedException e) concorrente {System.out.println("Errore P2");} {System.out.println("Errore P3");} {System.out.println("Errore P1");} {System.out.println("Errore P4");} {System.out.println("Errore P5");} p1.stop(); p2.stop(); p3.stop(); p4.stop(); p5.stop(); } } System.out.println(" Fine programma "); La classe data denisce le variabili condivise. /* data.java */ public class data { /********************* Processi: 4 5 1.3 Realizzazione in Java del calcolo concorrente P1 -> P2 -> P3 -> P4 -> P5 -> *********************/ x=3*a^3 y=2*b^3 z=3*d^3 t=y*z k=x+t public double x, y, z, t, k, a, b, d; public data(){ x=0; y=0; z=0; t=0; k=0; a=0; b=0; d=0; // variabili condivise // costruttori } } public data(double aa,double bb,double dd) { x=0; y=0; z=0; t=0; k=0; a=aa; b=bb; d=dd; } /* P2.java */ import java.lang.*; public class P1 extends Thread{ /** Calcola x=3*a^3 */ data item; private double y=3; // esponente private double c=3; // costante private double x; // base public P1(data d) { item=d; // passaggio dell'oggetto "data" a P1 x=d.a; // valore di 'a' } public void run(){ potenza p=new potenza(x,y); item.x=c*p.ans(); // x=3*a^3 System.out.println(" Calcolato x : " + item.x); } } /* P2.java */ public class P2 extends Thread{ /** Calcola y=2*b^3 */ data item; private double y=3; // esponente private double c=2; // costante private double x; // base public P2(data d) { item=d; // passaggio dell'oggetto "data" a P2 1.3 Realizzazione in Java del calcolo concorrente x=d.b; } // valore di 'b' } public void run(){ potenza p=new potenza(x,y); item.y=c*p.ans(); // y=2*b^3 System.out.println(" Calcolato y : " + item.y); } /* P3.java */ public class P3 extends Thread{ /** Calcola z=3*d^3 */ } data item; private double y=3; // esponente private double c=3; // costante private double x; // base public P3(data d) { item=d; // passaggio dell'oggetto "data" a P3 x=d.d; // valore di 'd' } public void run(){ potenza p=new potenza(x,y); item.z=c*p.ans(); // z=3*d^3 System.out.println(" Calcolato z : " + item.z); } /* P4.java */ public class P4 extends Thread{ /** Calcola t=y*z */ data item; private double y; private double z; public P4(data d) { item=d; // passaggio dell'oggetto "data" a P4 y=0; // l'assegnazione no va fatta al momento z=0; // dell'istanziazione, perche' ora 'd.y' e 'd.z' } // valgono entrambi '0'. public void run(){ y=item.y; // ora avviene l'assegnazione z=item.z; item.t=y*z; // t=y*z System.out.println(" Calcolato t : " + item.t); } } /* P5.java */ 6 7 1.4 Mutua esclusione con la primitiva 'scambia' public class P5 extends Thread{ /** Calcola k=x+t */ data item; private double x; private double t; public P5(data d) { item=d; // passaggio dell'oggetto "data" a P5 x=0; // valgono le stesse considerazioni fatte per P4 t=0; // in merito all'assegnazione dei valori alle variabili } public void run(){ x=item.x; t=item.t; item.k=x+t; // k=x+t System.out.println(" Calcolato il risultato finale k : " + item.k); } } /* potenza.java */ public class potenza { /** Calcola x^y */ private double esp; private double base; public potenza(double x, double y) { esp=y; base=x; } public double ans(){ return Math.pow(base,esp); } } 1.4 Mutua esclusione con la primitiva 'scambia' Si supponga che una architettura di un calcolatore ora al programmatore una istruzione atomica scambia(a,b), che realizza la seguente funzione: scambia(short &a, short &b){ short t; t = *a; *a = *b; *b = t; } La mutua esclusione si può ottenere usando la procedura atomica scambia nel seguente modo, in un contesto cioé di attesa attiva: Processo1{ b=1; while(true) { while(b==1) scambia(a,b); processo2{ c=1; while(true) { while(c==1) scambia(a,c); 8 1.5 Metodo synchronized per la mutua esclusione } } Sezione_critica1(); scambia(a,b); } } Sezione_critica1(); scambia(a,c); Ovviamente la variabile a é condivisa e inizializzata a zero. 1.5 Metodo synchronized per la mutua esclusione Si consideri un programma multithreaded in java costituito dai seguenti due thread, dove la variabile i è condivisa tra i thread stessi: public class scrivi1 extends Thread{ public void run(){ while(true){ i=1; System.out.println(thread1 i=+i); } } } public class scrivi2 extends Thread{ public void run(){ while(true){ i=2; System.out.println(thread2 i=+i); } } } Lo scopo dei due thread é semplicemente quello di scrivere 1 e 2 rispettivamente, ma ovviamente i context switch asincroni provocano degli interleaving per cui si possono avere delle scritture (errate) tipo: thread1 i=2 thread1 i=2 Un semplice modo per risolvere il problema può essere di denire una classe condivisa, chiamata diciamo S, come segue: public class S{ private int i; S(){i=0;} synchronized void s1(){ i=1; System.out.println(Sono il thread scrivi1: i=+i); } synchronized void s2(){ i=2; System.out.println(Sono il thread scrivi2: i=+i); } } Con questa classe, i thread scrivi1 e scrivi2 diventano: public class scrivi1 extends Thread{ S a; Scrivi1(S a){this.a=a;} public void run(){ while(true){ a.s1(); } } } public class scrivi2() extends Thread{ S b; Scrivi2(S a){this.b=a;} public void run(){ while(true){ b.s2(); } } 9 1.6 Un semplice problema di sincronizzazione 1.6 Un semplice problema di sincronizzazione Si supponga di avere un programma Java, costituito da due threads (riportati in seguito) che vengono attivati in concorrenza dal programma principale. I due thread condividono la variabile i. Si vuole che il programma produca una sequenza di numeri uguali, in particolare 5 5 5 5 etc. public class Inc extends Thread{ public void run(){ while(true){ System.out.println(i=+i); i++; } } } public class Dec extends Thread{ public void run(){ while(true){ System.out.println(i=+i); i--; } } } Ci si rende conto facilmente che questo codice puó non produrre la sequenza desiderata. Infatti, si supponga che la variabile i sia inizializzata a 5, e che ci sia un context switch dopo il while(true) del thread di sinistra, l'esecuzione del while(true) del thread di destra e un context switch subito dopo il while di destra. Cioè, il primo thread inizia, stampa 5, poi passa al secondo, che stampa 5 e ritorna al primo, che incrementa i che passa a 6 e stampa 6, poi passa al secondo che stampa 6 e decrementa etc. La sequenza stampata é pertanto: 556655 etc. Una possibile soluzione é, tralasciando le denizioni dei semafori: public class Inc()extends Threads{ public void run(){ while(true){ mutex.down(); s1.down(): System.out.println(i=+i); s2.up(); s3.down(); i++; mutex.up(); } } } public class Dec()extends Threads{ public void run(){ while(true){ s2.down(); System.out.println(i=+i); i--; s3.up(); s1.up(): } } } 1.7 Una soluzione semaforica al problema del produttore/consumatore 10 1.7 Una soluzione semaforica al problema del produttore/consumatore Questa soluzione usa 3 semafori: mutex per la mutua esclusione, vuoto che é un semaforo contatore che segnala il numero di spazi rimasti nell'array e pieno é un semaforo contatore che segnala il numero di elementi scritti nell'array. /* principale.java */ public class principale { private static data d= new data(); private static semaforo mutex= new semaforo(1); private static semaforo vuoto= new semaforo(10); private static semaforo pieno= new semaforo(0); } public static void main(String[] args) { P1 P= new P1(d,mutex,pieno,vuoto); P2 C= new P2(d,mutex,pieno,vuoto); P.start(); C.start(); } /* data.java */ public class data { public int IN; public int OUT; public int buf[]=new int[10]; public int conta; } public data() { IN=0; OUT=0; for(int i=0; i<10; i++) buf[i]=0; conta=0; } /* semaforo.java */ import java.lang.*; public class semaforo extends Thread{ private int s; public semaforo(int n) { s=n; } synchronized public void down(){ if (s<=0) { try{wait();} catch(InterruptedException e) {}; } else s--; } synchronized public void up(){ s++; notify(); 1.7 Una soluzione semaforica al problema del produttore/consumatore } } Il codice dei due thread, produttore e consumatore, segue: /* P1.java */ import java.lang.*; import java.io.*; public class P1 extends Thread { // produttore private data d; private semaforo mutex, pieno, vuoto; /** Creates a new instance of P1 */ public P1(data d, semaforo mutex, semaforo pieno, semaforo vuoto) { this.d=d; this.mutex=mutex; this.pieno=pieno; this.vuoto=vuoto; } public void run (){ int i=1; while(d.conta<50){ vuoto.down(); // vuoto va inizializzato a 10 mutex.down(); // mutex va inizializzato a 1 d.buf[d.IN]=i; System.out.println(" P ha inserito "+i+" in buf["+d.IN+"] \n"); d.IN=(d.IN+1)%10; i++; d.conta++; mutex.up(); pieno.up(); } } } /* P2.java */ import java.lang.*;import java.io.*; public class P2 extends Thread { // consumatore private data d; private int el; private semaforo mutex, pieno, vuoto; /** Creates a new instance of P2 */ public P2(data d, semaforo mutex, semaforo pieno, semaforo vuoto) { this.d=d; this.mutex=mutex; this.pieno=pieno; this.vuoto=vuoto; } public void run (){ while(d.conta<50){ pieno.down(); // pieno va inizializzato a 0 11 1.8 Una soluzione semaforica al problema dei lettori/scrittori } } mutex.down(); // mutex va inizializzato a 1 el=d.buf[d.OUT]; System.out.println(" C ha letto buf["+d.OUT+"]="+el+"\n"); d.OUT=(d.OUT+1)%10; d.conta++; mutex.up(); vuoto.up(); } 1.8 Una soluzione semaforica al problema dei lettori/scrittori import java.io.*; import java.lang.*; class Semaforo { int value; public Semaforo() { value = 1; } public Semaforo(int value) { this.value = value; } } public synchronized void up() { value++; notify(); } public synchronized void down() { while (value<=0) { try { wait(); } catch(InterruptedException e) { }; } value--; } class buffer { public Semaforo mutex = new Semaforo(1); public Semaforo blocco = new Semaforo(1); public int nlett; // numero di lettori 12 1.8 Una soluzione semaforica al problema dei lettori/scrittori 13 } class scrittore extends Thread { buffer dato; // dati condivisi int numero; // numero dello scrittore int cont; // numero di scritture public scrittore(buffer dato,int numero,int cont) { this.numero = numero; this.dato = dato; this.cont = cont; } public void run() { for (int i=0;i<cont;i++) // scrive "cont" volte { dato.blocco.down(); System.out.println("Scrittore numero "+numero+" ("+i+") Num lett "+dato.nlett); dato.blocco.up(); } } } class lettore extends Thread { buffer dato; int numero; // numero del lettore int cont; // numero di letture public lettore(buffer dato,int numero,int cont) { this.dato = dato; this.numero = numero; this.cont = cont; } public void run() { for (int i=0;i<cont;i++) { dato.mutex.down(); dato.nlett++; if (dato.nlett==1) dato.blocco.down(); dato.mutex.up(); System.out.println("Lettore numero "+numero+" ("+i+")"); dato.mutex.down(); dato.nlett--; if (dato.nlett == 0) dato.blocco.up(); dato.mutex.up(); } } } 1.9 Somma concorrente degli elementi di un array 14 public class main { public static void main(String argv[]) { buffer cont= new buffer(); lettore l1 = new lettore(cont,1,1000); // inzializzo lettori lettore l2 = new lettore(cont,2,1000); lettore l3 = new lettore(cont,3,1000); scrittore s1 = new scrittore(cont,1,5); // inizializzo scrittori scrittore s2 = new scrittore(cont,2,5); scrittore s3 = new scrittore(cont,3,5); l1.start(); // faccio partire i thread s1.start(); // in un ordine "misto" l2.start(); // per rendere la cosa piu' interessante s2.start(); l3.start(); s3.start(); } } 1.9 Somma concorrente degli elementi di un array Il programma esegue la somma concorrente di un array di 1000 elementi. Vengono realizzati tre thread, il primo dei quali somma i primi 500 elementi dell'array, il secondo somma i secondi 500 elementi e il terzo somma le due componenti. É un problema di sincronizzazione, risolto in questo caso con una primitiva semaforica. public class SommaConcorr { int array[] = new int[1000]; int primaMeta = 0; int secondaMeta = 0; int sommaTotale = 0; public Semaforo fineArray = new Semaforo(-999); public SommaConcorr() { for(int i=0; i<1000; i++){ array[i]=(int)(Math.random()*100); System.out.print(array[i] + " "); } SommaPrimaMeta prima = new SommaPrimaMeta(); SommaSecondaMeta seconda = new SommaSecondaMeta(); SommaTotale totale = new SommaTotale(); prima.start(); seconda.start(); totale.start(); } public static void main(String[] args) { SommaConcorr es = new SommaConcorr(); } 1.9 Somma concorrente degli elementi di un array class Semaforo extends Thread{ private int s; public Semaforo(int s){ this.s=s; } synchronized public void down(){ if (s<=0){ try{ wait(); } catch (InterruptedException e){ System.out.println(e); } } s--; } synchronized public void up(){ s++; notify(); } } class SommaPrimaMeta extends Thread{ int i = 0; public void run(){ while(i<500){ primaMeta = primaMeta + array[i]; fineArray.up(); i++; } } } class SommaSecondaMeta extends Thread{ int i = 500; public void run(){ while(i<1000){ secondaMeta = secondaMeta + array[i]; fineArray.up(); i++; } } } class SommaTotale extends Thread{ boolean attesa = true; public void run(){ while(attesa){ fineArray.down(); sommaTotale = primaMeta + secondaMeta; 15 1.10 Applicazione del problema dei lettori/scrittori alla sincronizzazione di un ponte } } } } 16 fineArray.up(); System.out.println("\nPrima metà: " + primaMeta); System.out.println("Seconda metà: " + secondaMeta); System.out.println("Somma totale: " + sommaTotale); attesa = false; 1.10 Applicazione del problema dei lettori/scrittori alla sincronizzazione di un ponte Un ponte a senso unico é attraversato da automobili in una sola direzione, e ci sono due semafori ai due punti d'accesso al ponte. Naturalmente se un semaforo é sul rosso l'altro é sul verde. In caso di mancanza di auto, dare la precedenza ad un lato del ponte a scelta del programmatore e, appena arriva un'auto, consentirne l'attraversamento. Se c'é almeno un'auto che sta attraversando il ponte in una certa direzione, il semaforo all'altro lato sará sul rosso per evitare l'attraversamento ad altre auto in direzione opposta. Sono consentite piú di una auto tutte nella stessa direzione. Quando tutte le auto sono passate e ci sono altre automobili in attesa sull'altro lato, il semaforo si inverte. Gli arrivi delle auto sono aleatori (funzione random), mentre il tempo di attraversamento é costante per ogni auto. La singola auto viene rappresentata con un thread di java e i due semafori con due semafori di java. import java.util.Vector; public class Ponte { public static final boolean DESTRA = true; public static final boolean SINISTRA = false; public static final int NUMERO_AUTOMOBILI = 10; Semaforo semaforoDestro = new Semaforo(1); Semaforo semaforoSinistro = new Semaforo(0); int transito = 0; int attesaDestra = 0; int attesaSinistra = 0; public Ponte() { } for(int i=0; i<NUMERO_AUTOMOBILI; i++){ if((Math.random()*10)<5){ Automobile a = new Automobile(DESTRA); a.start(); } else{ Automobile a = new Automobile(SINISTRA); a.start(); } } 1.10 Applicazione del problema dei lettori/scrittori alla sincronizzazione di un ponte public static void main(String[] args) { Ponte es = new Ponte(); } class Automobile extends Thread{ boolean lato; long tempo; public Automobile(boolean lato){ this.lato=lato; } public void run(){ try{ tempo = (long)(Math.random()*10*1000); this.sleep(tempo); if(lato==DESTRA){ System.out.println("\t\t\t\t\tArrivo a destra"); attesaDestra++; semaforoDestro.down(); attesaDestra--; transito++; semaforoDestro.up(); System.out.println("\t\t\t\t\tInizio transito da destra"); this.sleep(1000); semaforoDestro.down(); transito--; System.out.println("\t\t\t\t\tFine transito da destra"); if(transito==0 && attesaSinistra>0){ semaforoSinistro.up(); } else{ semaforoDestro.up(); } } else if(lato==SINISTRA){ semaforoDestro.down(); if(transito==0&&attesaDestra==0){ semaforoSinistro.up(); } else{ semaforoDestro.up(); } System.out.println("Arrivo a sinistra"); attesaSinistra++; semaforoSinistro.down(); attesaSinistra--; transito++; semaforoSinistro.up(); System.out.println("Inizio transito da sinistra"); this.sleep(1000); 17 1.11 Il prolema dei 5 loso mangiatori di spaghetti 18 semaforoSinistro.down(); transito--; System.out.println("Fine transito da sinistra"); if(transito==0){ semaforoDestro.up(); } else{ semaforoSinistro.up(); } } } } } } catch(InterruptedException e){ System.out.println(e); } class Semaforo extends Thread{ private int s; public Semaforo(int s){ this.s=s; } synchronized public void down(){ if (s<=0){ try{ wait(); } catch (InterruptedException e){ System.out.println(e); } } s--; } synchronized public void up(){ s++; notify(); } 1.11 Il prolema dei 5 loso mangiatori di spaghetti Riprendiamo questo problema di sincronizzazione. In questo esempio si assegna un semaforo ad ogni forchetta. import java.io.*; import java.lang.*; class Semaforo { int value; public Semaforo() { 1.11 Il prolema dei 5 loso mangiatori di spaghetti value = 1; } public Semaforo(int value) { this.value = value; } } public synchronized void up() { value++; notify(); } public synchronized void down() { while (value<=0) { try { wait(); } catch(InterruptedException e) { }; } value--; } class Filosofo extends Thread { Semaforo sx,dx; // semafori destro e sinistro int numero; // numero del filosofo public Filosofo(int numero,Semaforo sx, Semaforo dx) // costruttore { this.sx = sx; this.dx = dx; this.numero = numero; } public void run() { while(true) { //Pensa System.out.println("Filosofo "+numero+" pensa"); sx.down(); dx.down(); System.out.println("Filosofo "+numero+" mangia"); //mangia sx.up(); dx.up(); } } 19 1.11 Il prolema dei 5 loso mangiatori di spaghetti 20 } public class main public static { Semaforo s1 = Semaforo s2 = Semaforo s3 = Semaforo s4 = Semaforo s5 = Filosofo Filosofo Filosofo Filosofo Filosofo } f1 f2 f3 f4 f5 f1.start(); f2.start(); f3.start(); f4.start(); f5.start(); } = = = = = { void main(String argv[]) new new new new new Semaforo(1); // inizializza semafori Semaforo(1); Semaforo(1); Semaforo(1); Semaforo(1); new new new new new Filosofo(1,s1,s2); // inizializza filosofi Filosofo(2,s2,s3); Filosofo(3,s3,s4); Filosofo(4,s4,s5); Filosofo(5,s5,s1); // esegue thread Questa soluzione puó andare in stallo, come si vede da questa traccia di funzionamento: Filosofo 2 pensa Filosofo 3 pensa Filosofo 5 pensa Filosofo 4 pensa Filosofo 4 mangia Filosofo 4 pensa Filosofo 4 mangia Filosofo 4 pensa Filosofo 2 mangia Filosofo 4 pensa Filosofo 2 pensa --STALLO-- Come ultimo esempio, riprendiamo questo problema di sincronizzazione assegnado un semaforo ad ogni losofo. import java.io.*; import java.lang.*; class Semaforo { int value; public Semaforo() { value = 1; } public Semaforo(int value) { this.value = value; } public synchronized void up() { value++; notify(); 1.11 Il prolema dei 5 loso mangiatori di spaghetti } } public synchronized void down() { while (value<=0) { try { wait(); } catch(InterruptedException e) { }; } value--; } class Dati { char Stato[]; // array di stati Semaforo s[]; // array di semafori Semaforo mutex; public Dati() { Stato = new char[5]; s = new Semaforo[5]; for (int i=0;i<5;i++) s[i]= new Semaforo(); // crea semafori mutex = new Semaforo(); } } class Filosofo extends Thread { Dati dati; int i; public Filosofo(int i, Dati dati) { this.dati = dati; this.i = i; } public int Sx(int i) // semaforo di sinistra { return i; } public int Dx(int i) // semaforo di destra { if (i==4) return 1; else return (i+1); } 21 1.11 Il prolema dei 5 loso mangiatori di spaghetti } public void Prendif(int i) // Cerca di prendere una forchetta { dati.mutex.down(); dati.Stato[i]='A'; // stato di "Affamato" Test(i); dati.mutex.up(); dati.s[i].down(); } public void Rilasciaf(int i) // Rilascia la forchetta { dati.mutex.down(); dati.Stato[i] = 'P'; // stato di "Pensa" Test(Sx(i)); Test(Dx(i)); dati.mutex.up(); } public void Test (int i) // controlla se puo' prendere la forchetta { if (dati.Stato[i]=='A' && dati.Stato[Dx(i)]!='M' && dati.Stato[Sx(i)]!='M') { dati.Stato[i]='M'; // stato di "Mangia" dati.s[i].up(); } } public void run() { while(true) { //Pensa System.out.println("Filosofo "+i+" pensa"); Prendif(i); //Mangia System.out.println("Filosofo "+i+" mangia"); Rilasciaf(i); } } public class main { public static void main(String argv[]) { Dati dati = new Dati(); for(int i=0;i<5;i++) // inizializza stati filosofo dati.Stato[0]='P'; Filosofo f1 = new Filosofo(0,dati); // inizializza filosofi Filosofo f2 = new Filosofo(1,dati); Filosofo f3 = new Filosofo(2,dati); 22 1.11 Il prolema dei 5 loso mangiatori di spaghetti Filosofo f4 = new Filosofo(3,dati); Filosofo f5 = new Filosofo(4,dati); } f1.start(); f2.start(); f3.start(); f4.start(); f5.start(); } // esegue programma 23