Guido Massa Finoli Programmazione Java con Threads
Programmazione java con threads Indice: 1) Struttura dei Thread e modelli di ottimizzazione 2) Come si crea un Thread 3) Attività e Metodi della Classe Thread 4) Schedulazione dei Threads 5) Sincronizzazione dei Threads 6) Esempio tipico, problema produttore/consumatore 7) Particolarità dei Threads 8) Esempio di Automa Cellulare utilizzando Threads Caso A) Caso B) 9) Simulazione sistema automatico di produzione
1
Guido Massa Finoli Programmazione Java con Threads 1) Struttura dei Thread e modelli di ottimizzazione Un Thread è un modo per suddividere un programma in unità ancora più semplice, quindi è un modo per avere una unità di esecuzione ancora più elementare di un programma. Uno sviluppo senza Th associa ad un programma un processo, mentre la creazione di Th permette l’associazione di un processo al TH, quindi un programma suddiviso in Th viene eseguito su più processi che possono eseguiti contemporaneamente, contemporaneamente, utilizzando il servizio multitasking del sistema operativo. Questa suddivisione può essere effettuata su parti differenti del programma che possono essere eseguite contemporaneamente oppure una stessa parte che può essere eseguita su uno o più processi contemporaneamente. Questo permette la condivisione di risorse come la memoria, i servizi I/O ed un efficiente utilizzo della CPU. Ad esempio immaginiamo un programma che legge un file e lo elabora nella CPU, l’elaborazione con un PGM con 1 Th dura totalmente 7 secondi. 1 2 3 4 5 6 7 compu compute te Read Readfil file e comput compute e readfi readfile le compu compute te read readfil file e compu compute te CPU busy
b us y
b us y
busy
Come si vede la CPU è occupata solo per 4 secondi su 7, ovvero lo 0,57%. Se la stessa esecuzione la suddividiamo su 2 Th allora abbiamo nel primo caso un utilizzo totale della CPU di 14 sec., con una esecuzione reale di 8 sec. Con una efficienza del 57%. Nel caso di 2 Th avremo:teoricamente un utilizzo al 100% della CPU in quanto i due Th possono sovrapporsi nella loro esecuzione: CPU busy
b us y b us y
b us y b us y
busy busy
b us y
Quindi una programmazione a multi-thread ha sicuramente tutta una serie di vantaggi rispetto ad una programmazione senza Thread: 1) Miglioramente delle performance performance di di esecuzione esecuzione del programma 2) Tempi Tempi di attesa attesa dell’ute dell’utente nte ridotti ridotti 3) Uso efficiente efficiente delle delle risorse condivise del sistema 4) Una modellizzazione del programma programma più più semplice Ma sicuramente essa ha in se anche alcuni problemi, il principale è che il programmatore non deve solo analizzare l’esecuzione della procedura come in qualsiasi linguaggio, ma deve analizzare anche l’esecuzione dei singoli Th per verificare che siano tra di loro compatibili nell’utilizzo delle risorse, quindi organizzare un ordine di gerarchie, di computibilità, ecc. In definitiva occorre anche una programmazione dei Th il cui criterio finale è quello di ottimizzare tutte le variabili che gestiscono una procedura.
Risorse Accessibili: a) CPU b) I/O su disco 2
Guido Massa Finoli Programmazione Java con Threads c) I/O su Video Video d) Output Output su Stampa Stampante nte Quando un PGM viene avviato è sempre già presente un Thread in esecuzione, rappresentato dal TH Principale attivato dal main( String arg[]) ed esso ha 2 fondamentali aspetti: 1) Tutti i TH creati dal dal programmatore saranno figli del TH Main 2) Il TH Main deve essere essere sempre l’ultimo TH TH del PGM ad essere essere chiuso Il TH Main pur essendo automaticamente creato, può essere comunque gestito e controllato, ed innanzi tutto può essere acquisito attraverso il metodo: Static Thread currentThread()
2) Come si crea un Thread Attraverso la Classe Thread o una Interfaccia runnable 3
Guido Massa Finoli Programmazione Java con Threads Per creare un nuovo TH il PGM deve estendere la Classe Thread oppure implementare l’interfaccia runnable. Quindi data la classe Thread abbiamo: a) b) c) d)
Dichiarare Dichiarare una sottoc sottoclasse lasse di di Thread Thread Sovrascrivere il metodo run() Creare Creare un Ogge Oggetto tto della della sottocla sottoclasse sse Invocare Invocare il metodo metodo start() start()
public class Thread { public void run(){…}; }
Qundi abbiamo : Dichiarazione di una sottoclasse di Th e sovrascrittura metodo run() public class EsThread extends Thread { public void run(){…}; }
Creazione di un oggetto della sottoclasse di Th e chiamata di start() EsThread T = new EsThread(); T.start();
Dato l’interfaccia Runnable abbiamo: a) Creare una classe classe che implementa Runnable b) Creare un Oggetto della classe c) Creare un Oggetto della classe classe Thread Thread passando passando al costruttore l’Oggetto della classe classe implementazione di Runnable d) Attivare Attivare l’Oggetto l’Oggetto con il metodo metodo start() start() Creazione di una classe che implementa runnable() public class EsRunnable extends Runnable { public void run(){…}; }
Creazione oggetto di tipo runnable(), creazione di un oggetto di tipo Thread e passaggio come parametro dell’oggetto runnable() EsRunnable MioR = new EsRunnable; EsThread T = new EsThread(MioR); T.start();
3) Attività e Metodi della Classe Thread
4
Guido Massa Finoli Programmazione Java con Threads getName() get Priority() isAlive() oin() resume() run() sleep() start() suspend()
Ottiene il nome di un Thread Ottiene la priorità priorità di un Thread Determina Determina se un Thread è ancora attivo Esegue fino alla fine il Thread Riattiva Riattiva un Thread Punto di esecuzione esecuzione di un Thread Sospende Sospende un Thread per un tempo definito Attivazione Attivazione di un Thread Sospende Sospende un Thread per un tempo definito
Java thread can be in one of these states
new TH allocato allocato e in attesa di esecuzione esecuzione runnable TH in attesa di essere eseguito run TH in esecuzione esecuzione blocked TH in attesa di un evento esterno timed_waiting TH stato di attesa definito temporalmente dead TH finito
5
Guido Massa Finoli Programmazione Java con Threads
Come si vede il passaggio da uno stato e l’altro può essere dato dall’applicazione di un metodo, oppure dallo stato di utilizzo delle risorse disponibili. Quando non sono utilizzati esplicitamente dei metodi per gestire i Thread sarà la macchina ad occuparsi della loro esecuzione secondo criteri NON DETERMINISTICI e in parte dipendenti dal modo in cui sono stati configurati i Thread. Connessione tra Stati e Metodi meglio specificata:
Come si vede gli stati di un Thread (ready, running, slepping, blocked, waiting, dead) sono collegati a metodi oppure alla schedulazione in attesa di allocare una risorsa. Se non viene definita una priorità nella esecuzione o un ordine attraverso l’uso di metodi, l’elaborazione dei TH simultanei avviene in maniera casuale, non deterministica. 6
Guido Massa Finoli Programmazione Java con Threads Quando un thread è creato ( New Thread) nel suo stato iniziale è un empty Thread object; nessuna risorsa di sistema è allocata per esso. Quando un thread è in questo stato, su di esso può essere invocato il metodo start(); se start è invocato su un oggetto thread che non si trova in tale stato iniziale si causa l’eccezione Il metodo start crea le risorse di sistema necessarie per eseguire il thread, schedula il thread per eseguirlo, e chiama il metodo run() della classe thread; dopo il return di start il thread è Runnable
Un thread che inizializzato con start() è nello stato Runnable. In genere i computer hanno un solo processore, quindi i threads vanno eseguiti uno per volta.. Il Java runtime system deve implementare uno schema di scheduling scheme che permette la condivisione del processore fra tutti i "running" threads, così, ad un dato istante un solo “running” thread è realmente in esecuzione, gli altri sono in waiting per il loro turno di CPU.
Un thread diventa Not Runnable (o blocked) quando: 1) Il thread chiama il metodo wait() per aspettare che una specifica condizione sia soddisfatta. 2) Il thread è bloccato su un I/O. Per ogni entrata in un Not Runnable , c’è una specifica e distinta azione che permette il ritorno nello stato di Runnable: 1) Se un thread è stato posto in wait, il numero specifico di millisecondi deve trascorrere. 2) Se un thread è in attesa per una condizione, allora un altro oggetto deve notificare il thread in attesa di un cambio nelle condizioni chiamando notify o notifyAll 3) Se un thread è blocked su I/O, allora l’I/O deve essere completato Nota : Un programma non termina un thread attraverso la l a chiamata di un metodo; piuttosto, un thread termina naturalmente (dead) quando il metodo run termina (exit) isAlive()
Thread
New o Dead
isAlive() False True
Runnable o Not Runnable
7
Guido Massa Finoli Programmazione Java con Threads Primo Esempio: package Prove; import java.awt.*;
Thread { class MyThreadA extends Thread run() ) { // ent entry ry poi point nt for thr thread ead! ! public void run( (;;) { for (;;) System.out.println("hel .println("hello lo worl world1" d1"); ); } } } Thread { class MyThreadB extends Thread run() ) { // ent entry ry poi point nt for thr thread ead! ! public void run( (;;) { for (;;) System.out.println("hel .println("hello lo worl world2" d2"); ); } } } Prova1 { public class Prova1 main(Stri tring ng [] args) args) { public static void main(S MyThre MyThreadA adA t1 = new MyThreadA(); MyThre MyThreadB adB t2 = new MyThreadB(); t1.start(); t2.start(); // mai main n ter termin minate ates, s, but in Jav Java a the oth other er thr thread eads s kee keep p run runnin ning! g! // and hen hence ce Jav Java a pro progra gram m con conti tinue nues s run runnin ning! g! } }
Una volta attivati con il metodo start() i due TH verranno elaborati casualmente. yield() Esempio 2 package Prove; import java.awt.*;
Thread { class EsThread extends Thread nome; private String nome; EsThread(String nome){ public EsThread(String nome; ; this.nome = nome } run() ) { // ent entry ry poi point nt for thr thread ead! ! public void run( (;;) { for (;;) System.out.println("hello" .println("hello" + nome); nome); (); yield (); } } } Prova2 { public class Prova2 main(Stri tring ng [] args) args) { public static void main(S EsTh EsThre read ad t1 = new EsThread("aaaaa" EsThread("aaaaa"); ); EsTh EsThre read ad t2 = new EsThread("KKKKKK" EsThread("KKKKKK"); ); t1.start(); t2.start(); // mai main n ter termin minate ates, s, but in Jav Java a the oth other er thr thread eads s kee keep p run runni ning! ng! // and hen hence ce Jav Java a pro progra gram m con conti tinue nues s run runnin ning! g!
8
Guido Massa Finoli Programmazione Java con Threads } } helloaaaaa helloKKKKKK helloaaaaa helloKKKKKK helloaaaaa helloKKKKKK helloaaaaa helloKKKKKK helloaaaaa helloKKKKKK helloaaaaa
In questo caso invece il metodo yield() , determina il passaggio del Th che ha yield() allo stato runnable, permettendo in tal modo l’esecuzione dell’altro Th, ma non è detto che il TH che lo sostituisce nello stato run non sia lo stesso. j oi n () package Prove; import java.lang.Math;
Thread { class MathSin extends Thread deg; ; public double deg res; ; public double res degree) ) { public MathSin(int degree deg = degree degree; ; } run() ) { public void run( System.out.println("Gr .println("Gradi adi " + deg deg); ); Deg2Rad d = Math. Math.toRadians(deg deg); ); double Deg2Ra res = Math Math. .sin(Deg2Rad); System.out.println(" .println("Se Seno no = "+res res); ); } } Thread { class MathCos extends Thread deg; ; public double deg res; ; public double res degree) ) { public MathCos(int degree deg = degree degree; ; } run() ) { public void run( System.out.println("Gr .println("Gradi adi "+deg deg); ); Deg2Rad d = Math. Math.toRadians (deg deg); ); double Deg2Ra res = Math Math. .cos(Deg2Rad); System.out.println("Co .println("Cosen seno o "+res res); ); } } Thread { class MathTan extends Thread deg; ; public double deg res; ; public double res degree) ) { public MathTan(int degree deg = degree degree; ; } run() ) { public void run( System.out.println("Gr .println("Gradi adi "+deg deg); ); Deg2Rad d = Math. Math.toRadians (deg deg); ); double Deg2Ra res = Math Math. .tan(Deg2Rad); System.out.println("Tan .println("Tangent gente e "+res res); ); }
9
Guido Massa Finoli Programmazione Java con Threads } Prova3 { class Prova3 main(String ring args[]) args[]) { public static void main(St Math MathSi Sin n st = new MathSin(45); Math MathCo Cos s ct = new MathCos(60); Math MathTa Tan n tt = new MathTan(30); st.start(); ct.start(); tt.start(); wait it fo for r co comp mple leti tion on of al all l th thre read ad an and d th then en su sum m try { // wa st.join(); ct.join(); //wai //wait t for com comple pletio tion n of Mat MathCo hCos s obj object ect tt.join(); .res + ct. ct.res + tt.res t.res; ; double z = s t .res System.out.println("S .println("Som omma ma di se seno no, , co cose seno no e ta tang ngen ente te "+z); } (Interrupted ptedExce Exceptio ption n IntExp) IntExp) { catch(Interru } } } Gradi Gradi 60.0 Gradi Gradi 30.0 Gradi Gradi 45.0 Tangente 0.5773502691896257 0.5773502691896257 Seno = 0.707106 0.7071067811 78118654 865475 75 Coseno 0.5000000000000001 0.5000000000000001 Somma Somma di seno, seno, coseno coseno e tangen tangente te 1.7844 1.7844570 570503 503761 761732 732
In questo caso l’utilizzo del metodo join() permette di avere il calcolo indipendente e casuale nell’ordin nell’ordinee del seno , del coseno coseno e della tangente tangente ma ogni TH una volta eseguito eseguito si mette in uno stato di bloccato fino a quando tutti i TH sono eseguiti e bloccati e il sistema ritorna al TH principale. setPriority()
L’esecuzione con priorità permette di assegnare una priorità di esecuzione al TH attraverso la funzione setPriority() , la priorità può andare da 1 a 10con i valori generalizzati: MIN_PR MIN_PRIOR IORITY ITY = 1, NORM_P NORM_PRIO RIORIT RITY Y = 5, MAX_PR MAX_PRIOR IORITY ITY = 10 package Prove;
/* Thr Thread eadPri Priori orityD tyDemo emo.ja .java: va: A pro progra gram m whi which ch sho shows ws alt alteri ering ng ord order er of thr thread eads s by cha changi nging ng pri priori ority. ty. */ Thread { class A extends Thread run() ) { public void run( System.out.println("I .println("Ini nizi zio o TH A" A"); ); for(int i = 1 ; i < = 4 ; i + + ) { System.out.println("\ .println("\t t Fr From om Th Thre read adA: A: i= " + i); } System.out.println("Fi .println("Fine ne A" A"); ); } } Thread { class B extends Thread run() ) { public void run( System.out.println("I .println("Ini nizi zio o TH B" B"); ); for(int j = 1 ; j < = 4 ; j + + ) { System.out.println("\ .println("\t t Fr From om Th Thre read adB: B: j= " + j); } System.out.println("Fi .println("Fine ne B" B"); ); }
10
Guido Massa Finoli Programmazione Java con Threads } Thread { class C extends Thread run() ) { public void run( System.out.println("I .println("Ini nizi zio o TH C" C"); ); for(int k = 1 ; k < = 4 ; k + + ) { System.out.println("\ .println("\t t Fr From om Th Thre read adC: C: k= " + k); } System.out.println("Fi .println("Fine ne C" C"); ); } } Prova4 { public class Prova4 main(String ring args[]) args[]) { public static void main(St A thre thread adA A = new A(); B thre thread adB B = new B(); C thre thread adC C = new C(); threadC.setPriority(Thread. MAX_PRIORITY ); threadB.setPriority(th threadB.setPriority(threadA.getPr readA.getPriority() iority() + 1); threadA.setPriority(Thread. MIN_PRIORITY ); System.out.println("I .println("Ini nizi zio o TH A" A"+ + threadA.getPriority()) threadA.getPriority()); ; threadA.start(); System.out.println("I .println("Ini nizi zio o TH B" B"+ + threadB.getPriority()) threadB.getPriority()); ; threadB.start(); System.out.println("I .println("Ini nizi zio o TH C" C"+ + threadC.getPriority()); threadC.getPriority()); threadC.start(); System.out.println("Fi .println("Fine ne TH mai main" n"); ); } } Iniz Inizio io TH A5 Iniz Inizio io TH B6 Iniz Inizio io TH A From From Thre Thread adA: A: From From Thre Thread adA: A: From From Thre Thread adA: A: From From Thre Thread adA: A: Fine Fine A Iniz Inizio io TH C10 C10 Iniz Inizio io TH C From From Thre Thread adC: C: From From Thre Thread adC: C: From From Thre Thread adC: C: From From Thre Thread adC: C: Fine Fine C Fine Fine TH main main Iniz Inizio io TH B From From Thre Thread adB: B: From From Thre Thread adB: B: From From Thre Thread adB: B: From From Thre Thread adB: B: Fine Fine B
i= i= i= i=
1 2 3 4
k= k= k= k=
1 2 3 4
j= j= j= j=
1 2 3 4
Occorre notare come il TH iniziale può essere C, B e anche A. In definitva il TH da cui iniziare non è detto che sia quello con la priorità massima. interrupt()
Un thread può essere interrotto invocando su di esso il metodo interrupt(); tale metodo rappresenta però solo una segnalazione per il thread che lo riceve: a) se il thread stava lavorando, continuerà a farlo b) se il thread stava in wait(), viene sollevata un’eccezione 11
Guido Massa Finoli Programmazione Java con Threads c) se il thread era bloccato su un’operazione di I/O.Su alcuni sistemi si potrebbe ottenere l’eccezione, ma generalmente il thread resta bloccato e interrupt non ha effetto
12
Guido Massa Finoli Programmazione Java con Threads 4) Schedulazione dei Threads L’esecuzione di thread multipli su una singola CPU, in qualche ordine, è detto scheduling. Il Java runtime supporta un semplice algoritmo di scheduling deterministico conosciuto come fixed priority scheduling, che schedula i threads sulla base delle loro priorità relative agli altri runnable threads. Quando un thread viene creato creato esso eredita la sua priorita dal thread che lo ha creato creato È poss possib ibile ile modi modific ficar aree la prio priori rità tà di un thre thread ad in qual qualsi sias asii mome moment ntoo dopo dopo la sua sua crea creazio zione ne utili utilizz zzan ando do il meto metodo do setPriority() Le prio priori rità tà dei Thre Threaad sono ono inte interi ri comp ompresi resi fra fra MIN_PRIORITY e MAX_PRIORITY (constanti definite nelle classe Thread); il valore intero più grande corrisponde alla più alta priorità Ad un dato istante, quando più thread sono pronti per essere eseguiti, il runtime system sceglie il thread runnable con la più alta priorità per l’esecuzione. Solo quando il thread finisce o diventa not runnable per qualche ragione il thread di priorita minore viene eseguito Il thread scelto sarà eseguito fino a quando: 1) un thread con più alta priorità non diventa runnable. 2) il suo metodo run esiste. 3) su un sistema con time-slicing, il suo slice è terminato Ad ogni istante il thread di massima priorità dovrebbe essere in esecuzione. Comunque, ciò non è garantito. Inoltre, un dato thread può in un qualsiasi momento cedere i suoi diritti di esecuzione invocando il metodo yield; la cessione vale solo per thread con la stessa priorità. Thread possiede anche metodi deprecated : s top(), ma è preferibile terminare il thread quando termina il metodo run() suspend() e resume(), che possono provocare deadlock
13
Guido Massa Finoli Programmazione Java con Threads 5) Sincronizzazione dei Threads Uno dei problemi nella gestione dei TH è quello del loro controllo quando utilizzano risorse condivise. La soluzione prospettata in java è quella del Monitor o Box, per cui un solo TH può accedere ad un Box e rimanere in esso fino a quando non viene completato. Esempi di risorse condivise sono: 1) Il Proble Problema ma di Lettura/Sc Lettura/Scrittura rittura 2) Il Problema Produttore/Consumatore Il problema lettura/scrittura si ha quando più TH cercano di accedere ad una stessa risorsa per leggere e per scrivere, è evidente che in tal caso va messo un semaforo che permetta ad un TH di non soppiantare un altro fino a quando esso non abbia finito l’operazione. Questo è reso possibile dalla parola chiave synchronized che va messo come specifica del metodo del TH override di run(). Nell’esempio sotto può essere notata la differenza tra una call(..) non sicronizzata e una call(..) sincronizzata. Nel primo caso si ha uno scenario race per cui è molto probabile che immesso un TH esso venga interrotto prima del completamento per fare posto agli altri successivi. Mentre con l’opzione synchronized il TH in start() non viene sostituito prima che sia completato. Esempio package Prove;
/* Prog Programm ramma a sinc sincroni ronizzat zzato o */ CallMe { class CallMe call(Strin ring g msg) msg) { synchron sync hronized ized void call(St System.out.print("[" print("[" + msg) msg); ; try{ Thread.sleep (1000); } catch (Interru (Interrupted ptedExce Exceptio ption n e ){ System.out.println("Interrotto" .println("Interrotto"); ); } System.out.println("]" .println("]"); ); } } Runnable e { class Caller implements Runnabl String msg msg; ; CallMe target; target; Thread t; Caller(Ca (CallM llMe e targ, targ, String String s) { public Caller target = targ; targ; msg = s; t = new Thread(this); t.start(); } run() ) { public void run( target.call( target .call(msg msg); ); } } Prova5 { class Prova5 main(String ing args[]) args[]) { public static void main(Str CallMe CallMe targe target t = new CallMe(); Call Caller er ob1 ob1 = new Caller(target, "Ciao"); "Ciao"); Call Caller er ob2 ob2 = new Caller(target, "Sincronizzato"); "Sincronizzato"); Call Caller er ob3 ob3 = new Caller(target, "Mondo"); "Mondo"); try{
14
Guido Massa Finoli Programmazione Java con Threads ob1.t ob1.t.join(); ob2.t ob2.t.join(); ob3.t ob3.t.join(); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } } } [Ciao] [Mondo] [Sincronizzato]
Come si vede Come vede vien vienee scel scelta ta l’op l’opzio zione ne este estens nsio ione ne di Ru Runn nnab able le per per crea creare re il Thre Thread ad e pass passar aree all’override run(). Viene creata prima una istanza della classe CallMe che contiene il metodo call(), poi 3 istanze della classe Caller estensione di Runnable a cui viene passata p assata l’istanza l’i stanza di CallMe e il messaggio. Quando viene creata l’estanza di Caller viene creato anche il TH associato e viene fatto partire, con l’override su run() che chiama dall’istanza CallMe passata il metodo call() con il messaggio. ob1.t ob1.t.start(); ob2.t ob2.t.start(); ob3.t ob3.t.start(); System.out.println("S .println("S "+ ob1. ob1.t t.getState()+ " " + ob2.t ob2.t.getStat .getState() e() + " " + ob3. ob3.t t.getState()); ob1.t ob1.t.run(); System.out.println("S .println("S "+ ob1. ob1.t t.getState()+ " " + ob2.t ob2.t.getStat .getState() e() + " " + ob3. ob3.t t.getState()); ob3.t ob3.t.run(); ob1.t ob1.t.run(); System.out.println("S .println("S "+ ob1. ob1.t t.getState()+ " " + ob2.t ob2.t.getStat .getState() e() + " " + ob3. ob3.t t.getState());
Se proviamo a gestire dal main i TH e controllando lo stato dei TH nel caso sincronizzato, avremo una possibile situazione del genere: S RUNNABLE RUNNABLE RUNNABLE RUNNABLE RUNNABLE RUNNABLE [Ciao] [MondoS [MondoS BLOCKED BLOCKED BLOCKED BLOCKED TIMED_WA TIMED_WAITIN ITING G ] [Sincronizzato] [Ciao] [Mondo] S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TERMINAT TERMINATED ED
Ovvero ogni TH termina il metodo call(), BLOCCANDO gli altri, e rimanendo in WAITING quando il suo ciclo contiene un sleep() come nel caso di call(). Se invece poniamo il metodo call() NON sincronizzato allora avremo una esecuzione race, con vari possibili scenari, ad esempio: [Ciao[SincronizzatoS [Ciao[SincronizzatoS TIMED_WAITING TIMED_WAITING TIMED_WAITING RUNNABLE [Ciao[Mondo] ] ] S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TIMED_WA TIMED_WAITIN ITING G [Mondo]
15
Guido Massa Finoli Programmazione Java con Threads ] S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TERMINAT TERMINATED ED
Come si vede in questo caso il TH1 quando è andato di sleep() ha permesso l’entrata in esecuizione del TH2, che a sua volta andato in sleep() ha rimandato il controllo al main permettendo la scrittura dello stato. IL TH che riparte non è tra quelli in coda o il TH3 ma è un nuovo TH1 attivato con run(), poi viene eseguito il TH3 e poi di nuovo il TH3. Se proviamo a rilanciarlo più volte vediamo come lo scenario cambia in continuazione: S RUNNABLE RUNNABLE RUNNABLE RUNNABLE RUNNABLE RUNNABLE [Mondo[Sincronizzato[Ciao[Ciao] ] ] ] S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TERMINAT TERMINATED ED S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TERMINAT TERMINATED ED [Ciao[Sincronizzato[Mo [Ciao[Sincronizzato[MondoS ndoS RUNNABLE TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING [Ciao] ] ] ] S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TERMINAT TERMINATED ED S TERMINAT TERMINATED ED TERMINAT TERMINATED ED TERMINAT TERMINATED ED
Proviamo adesso a indicare una situazione nella quale il TH ha un ciclo infinito
16
Guido Massa Finoli Programmazione Java con Threads 6) Esempio tipico, problema produttore/consumatore: Una applicazione Java ha un thread (produttore) che scrive dati su un file mentre un secondo thread (consumatore) legge i dati dallo stesso file. Digitando caratteri sulla tastiera il thread produttore pone l’evento key in una coda di eventi e il thread consumatore consumer legge gli eventi dalla stessa coda. Ambedue questi esempi coinvolgono thread concorrenti che condividono una risorsa comune (File e coda di eventi), pertanto i thread devono essere sincronizzati. L’attività di Producer e Consumer può essere sincronizzata con due strumenti: A) I due thread non devono accedere simultaneamente all’oggetto Box, quindi serve un lock B) I due thread devono coordinarsi. Il Producer deve indicare al Consumer che il valore è pronto e il Consumer deve avere un modo per notificare che il valore è stato prelevato. La classe Thread ha una collezione di metodi (wait, notify, e notifyAll) che consentono ai threads di aspettare una condizione e notificare ad altri threads che la condizione è cambiata. Il lock serve per regolare l’accesso alla risorsa condivisa; il lock tuttavia non garantisce nessun ordine ordine nelle operazioni, operazioni, ma solo il mutex, per questo serve anche anche il coordinam coordinamento ento Il segmento segmento di codice entro un programma che accede ad un oggetto da diversi threads concorrenti è chiamato “regione critica”. Le regioni critiche possono essere un blocco di istruzioni o un metodo e sono identificate dalla keyword synchronized(): Posta prima delle istruzioni in accordo alla sintassi synchronized (expr) {
}, dove expr è un riferimento all’oggetto che sarà locato oppure nella dichiarazione del metodo (public int synchronized metodo…). La prima scelta potrebbe essere la migliore, perché raffina il livello di granularità, riducendo l’acqu l’acquisiz isizion ionee del lock lock solo solo alle alle istruzi istruzioni oni strett strettame amente nte neces necessar sarie, ie, ed inoltre inoltre la prima prima scelta scelta permette di effettuare la sincronizzazione anche su oggetti diversi da this, specificandoli nella expr. Il metodo, d’altra parte, potrebbe essere una buona scelta progettuale per localizzarvi tutto il codice Sincronizzato. Java associa un lock ad ogni oggetto che ha del codice synchronized. I lock sono acquisiti per thread, quindi se un metodo sincronizzato chiama un altro metodo sincronizzato, non si cerca di ottenere altri lock; in tal modo, è possibile effettuare chiamate anche ricorsive, e/o di metodi ereditati senza incorrere in blocchi o deadlock perché il lock acquisito resta sempre uno solo In ogni momento è possibile sapere se un oggetto è lockato da un thread invocando dal codice di tale thread il metodo statico holdslock della classe thread, che come parametro accetta l’oggetto e restituisce un booleano che indica l’acquisizione del lock. Quando una classe estesa opera l’overriding di un metodo sincronizzato, può anche non imporre la sincronizzazione nel metodo sovrascrivente; esso risulterà però automaticamente sincronizzato se invoca super P = Op Oper eraz azio ione ne Produ rodutt ttor oree C = Op Oper eraz azio ione ne Cons Consum umat ator oree 1) Ogni prodotto immesso deve essere consumato 2) Un prodotto immesso NON può essere consumato 2 volte
17
Guido Massa Finoli Programmazione Java con Threads
Per rendere questo meccanismo più facilmente comprensibile creiamo un Th produttore che chiama un metodo put2(), di una classe Coda2 con oggetto comune, esso si occupa di aggiungere 1 ad una variabile condivisa, inoltre l’aggiornamento avverrà solo quando il semaforo è a false, altrimenti in esso si attiverà un loop che terrà il Th bloccato su quella risorsa. Lo stesso avverrà per il metodo get2(), dove si avrà valore – 1, e il loop attivo a true.
P
C
True
False
P_ESEC D = true
C_ESEC D = false
Se la variabile D = true il ciclo P sarà il loop, ma siccome i due Th creati sono concorrenti ogni tanto uno uscirà dal loop per timeslice, e sarà eseguito l’altro ciclo che trovando la variabile D a true eseguirà la parte successiva del codice C_ESEC e metterà D a false, ora se il loop fosse eseguito solo dal controllo di D, il loop P non sarebbe eseguito e di conseguenza sarebbe eseguito il codice P_ESEC. Quindi con un semplice
18
Guido Massa Finoli Programmazione Java con Threads loop di controllo della variabile semaforo D, e con soli 2 Th attivi uno consumatore e uno produttore vi sono buone possibilità che il i l sistema esegua sempre P e C alternativamente. Ma se proviamo a mettere nel ciclo while del loop gestito dalla variabile D un ciclo annidato fatto da un contatore fino ad esempio 30, vedremo che accadrà una cosa molto singolare. Che quando il ciclo va in timeslice in P con D = true viene eseguito C_ESEC messo D a false, ma quando rientra P esso è ancora nel ciclo for di P malgrado D = false, e quindi P_ESEC non verrà eseguito, ma al nuovo timeslice verrà eseguito di nuovo C_ESEC. Se poniamo un valore 1 che indica se il ciclo è in P, e -1 se è in C, possiamo monitorare chiaramente il processo che avviene nel sistema. main(Stri tring ng [] args) args) { public static void main(S Coda Coda2 2 co = new Coda2(); Produt Produttor tore e pro = new Produttore(co); Consum Consumato atore re con = new Consumatore(co); pro.start(); con.start(); } valore; private int valore; private boolean disponibile = false ; private long count = 0; get2() ) { public int get2( while (disponibile == false ){ i=0; i <= 30; 30; i++) i++){ { for(int i=0; count = - 1 ; if (i >= 30) { disponibile = false ; } } } valore = valore - 1; disponibile = false ; System.out.print("G .print("Get et __ __# # "+ valore + " c "+ count + " " + disponibile); disponibile); count = 0; valore; return valore; } put2() ) { public int put2( while (disponibile == true){ i=0; i<= i<= 30; 30; i++) i++){ { for(int i=0; count = + 1 ; if (i >= 30) { disponibile = true; } } } valore = valore + 1; disponibile = true; System.out.print("P .print("Put ut __ __# # "+ valore+ valore+ " c " + count + " " + disponibile); disponibile); count = 0; valore; return valore; } } Put Put Get Get Put Put Put Put Put Put
__# __# __# __# __# __# __# __# __# __#
1 0 1 2 3
c c c c c
0 true true Prod Produt utto tore re __# __# 1 1 fals false e Cons Consum umat ator ore e __# __# 0 -1 true true Prod Produt utto tore re __# __# 1 -1 true true Prod Produt utto tore re __# __# 2 -1 true true Prod Produt utto tore re __# __# 3
19
Guido Massa Finoli Programmazione Java con Threads Put Put __# __# 4 c -1 true true Prod Produt utto tore re __# __# 4 Put Put __# __# 5 c -1 true true Prod Produt utto tore re __# __# 5 (com (come e si vede vede il cicl ciclo o get get è rima rimast sto o nel nel for for infa infatt tti i il valo valore re coun count t è -1, -1, ma il semaforo è su true in quanto il Th è uscito dal ciclo for per timeslice, e questo questo ha permes permesso so al ciclo ciclo put di esegui eseguire re dirett direttame amente nte la proced procedura ura P_ESEC P_ESEC che incr increm emen enta ta il cont contat ator ore e e mett mette e il sema semafo foro ro a true true. . Ma malg malgra rado do il valo valore re del del semaforo sia a true quando ritorna al ciclo get non viene effettuato le istr istruz uzio ione ne C_ES C_ESEC EC in quan quanto to il Th si trov trova a anco ancora ra nel nel cicl ciclo o for) for). . Get Get __# __# 4 c 1 fals false e Cons Consum umat ator ore e __# __# 4 Get Get __# __# 3 c 1 fals false e Cons Consum umat ator ore e __# __# 3 Get Get __# __# 2 c 1 fals false e Cons Consum umat ator ore e __# __# 2 Put Put __# __# 3 c -1 true true Prod Produt utto tore re __# __# 3 Get Get __# __# 2 c 1 fals false e Cons Consum umat ator ore e __# __# 2 Get Get __# __# 1 c 0 fals false e Cons Consum umat ator ore e __# __# 1 Get Get __# __# 0 c 1 fals false e Cons Consum umat ator ore e __# __# 0 Get Get __# __# -1 c 1 fals false e Cons Consum umat ator ore e __# __# -1 Put Put __# __# 0 c -1 true true Prod Produt utto tore re __# __# 0 Get Get __# __# -1 c 1 fals false e Cons Consum umat ator ore e __# __# -1 Put Put __# __# 0 c -1 true true Prod Produt utto tore re __# __# 0 Put Put __# __# 1 c -1 true true Prod Produt utto tore re __# __# 1 Get Get __# __# 0 c 1 fals false e Cons Consum umat ator ore e __# __# 0 Put Put __# __# 1 c -1 true true Prod Produt utto tore re __# __# 1 Put Put __# __# 2 c -1 true true Prod Produt utto tore re __# __# 2 Put Put __# __# 3 c -1 true true Prod Produt utto tore re __# __# 3 Put Put __# __# 4 c -1 true true Prod Produt utto tore re __# __# 4 Put Put __# __# 5 c -1 true true Prod Produt utto tore re __# __# 5 Get Get __# __# 4 c 1 fals false e Cons Consum umat ator ore e __# __# 4 Get Get __# __# 3 c 1 fals false e Cons Consum umat ator ore e __# __# 3 Put Put __# __# 4 c -1 true true Prod Produt utto tore re __# __# 4 Put Put __# __# 5 c -1 true true Prod Produt utto tore re __# __# 5 Put Put __# __# 6 c -1 true true Prod Produt utto tore re __# __# 6 Put Put __# __# 7 c -1 true true Prod Produt utto tore re __# __# 7 Get Get __# __# 6 c 1 fals false e Cons Consum umat ator ore e __# __# 6 Get Get __# __# 5 c 1 fals false e Cons Consum umat ator ore e __# __# 5 Get Get __# __# 4 c 1 fals false e Cons Consum umat ator ore e __# __# 4 Get Get __# __# 3 c 1 fals false e Cons Consum umat ator ore e __# __# 3 Get Get __# __# 2 c 1 fals false e Cons Consum umat ator ore e __# __# 2 Get Get __# __# 1 c 1 fals false e Cons Consum umat ator ore e __# __# 1 Put Put __# __# 2 c -1 true true Prod Produt utto tore re __# __# 2 Put Put __# __# 3 c -1 true true Prod Produt utto tore re __# __# 3 Put Put __# __# 4 c -1 true true Prod Produt utto tore re __# __# 4 Put Put __# __# 5 c -1 true true Prod Produt utto tore re __# __# 5 Put Put __# __# 6 c -1 true true Prod Produt utto tore re __# __# 6 Get Get __# __# 5 c 1 fals false e Cons Consum umat ator ore e __# __# 5 Put Put __# __# 6 c -1 true true Prod Produt utto tore re __# __# 6 Get Get __# __# 5 c 1 fals false e Cons Consum umat ator ore e __# __# 5 Get Get __# __# 4 c 1 fals false e Cons Consum umat ator ore e __# __# 4 Get Get __# __# 3 c 1 fals false e Cons Consum umat ator ore e __# __# 3 Get Get __# __# 2 c 1 fals false e Cons Consum umat ator ore e __# __# 2 Get Get __# __# 1 c 1 fals false e Cons Consum umat ator ore e __# __# 1 Put Put __# __# 2 c -1 true true Prod Produt utto tore re __# __# 2 Put Put __# __# 3 c -1 true true Prod Produt utto tore re __# __# 3 Put Put __# __# 4 c -1 true true Prod Produt utto tore re __# __# 4 Put Put __# __# 5 c -1 true true Prod Produt utto tore re __# __# 5 Get Get __# __# 4 c 1 fals false e Cons Consum umat ator ore e __# __# 4 Get Get __# __# 3 c 1 fals false e Cons Consum umat ator ore e __# __# 3 Get Get __# __# 2 c 1 fals false e Cons Consum umat ator ore e __# __# 2 Get Get __# __# 1 c 1 fals false e Cons Consum umat ator ore e __# __# 1 Get Get __# __# 0 c 1 fals false e Cons Consum umat ator ore e __# __# 0 Get Get __# __# -1 c 1 fals false e Cons Consum umat ator ore e __# __# -1 Get Get __# __# -2 c 1 fals false e Cons Consum umat ator ore e __# __# -2 Put Put __# __# -1 c -1 true true Prod Produt utto tore re __# __# -1 Get Get __# __# -2 c 1 fals false e Cons Consum umat ator ore e __# __# -2
20
Guido Massa Finoli Programmazione Java con Threads Put Put Get Get Put Put Put Put Get Get Get Get Put Put Put Put Put Put Put Put Put Put Put Put Get Get Get Get Get Get Put Put Get Get
__# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __# __#
-1 c -1 true true Prod Produt utto tore re __# __# -1 -2 c 1 fals false e Cons Consum umat ator ore e __# __# -2 -1 c -1 true true Prod Produt utto tore re __# __# -1 0 c -1 true true Prod Produt utto tore re __# __# 0 -1 c 1 fals false e Cons Consum umat ator ore e __# __# -1 -2 c 1 fals false e Cons Consum umat ator ore e __# __# -2 -1 c -1 true true Prod Produt utto tore re __# __# -1 0 c -1 true true Prod Produt utto tore re __# __# 0 1 c -1 true true Prod Produt utto tore re __# __# 1 2 c -1 true true Prod Produt utto tore re __# __# 2 3 c -1 true true Prod Produt utto tore re __# __# 3 4 c -1 true true Prod Produt utto tore re __# __# 4 3 c 1 fals false e Cons Consum umat ator ore e __# __# 3 2 c 1 fals false e Cons Consum umat ator ore e __# __# 2 1 c 1 fals false e Cons Consum umat ator ore e __# __# 1 2 c -1 true true Prod Produt utto tore re __# __# 2 1 c 1 fals false e Cons Consum umat ator ore e __# __# 1
Cosa accade se sincronizziamo i 2 metodi put() e get()? Accadrà che i 2 Th lavoreranno in alternativa sull’oggetto coda, e questo comporterà che get e put saranno sempre alternati.
main(Stri tring ng [] args) args) { public static void main(S Coda Coda2 2 co = new Coda2(); Produt Produttor tore e pro = new Produttore(co); Consum Consumato atore re con = new Consumatore(co); pro.start(); con.start();
} valore; private int valore; private boolean disponibile = false ; private long count = 0; get2() { public synchronize synchronized d int get2() while (disponibile == false ){ i=0; i <= 3; i++) i++){ { for(int i=0; count = - 1 ; if ( i > = 3 ) { disponibile = false ;
}
} try {
wait(); }catch (InterruptedExcepti (InterruptedException on e){ } } valore = valore - 1; disponibile = false ; System.out.println("G .println("Get et __ __# # "+ valore + " c "+ count + " " + disponibile); disponibile); count = 0; notifyAll(); valore; return valore; }
21
Guido Massa Finoli Programmazione Java con Threads put2() { public synchronize synchronized d int put2() ( disponibile == while true){ i=0; i<= i<= 3; i++) i++){ { for(int i=0; count = + 1 ; if ( i > = 3 ) { disponibile = true; } } try {
wait(); }catch (InterruptedExceptio (InterruptedException n e){ } } valore = valore + 1; disponibile = true; System.out.println("P .println("Put ut __ __# # "+ valore+ valore+ " c " + count + " " + disponibile); disponibile); count = 0; notifyAll(); valore; return valore; } } Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Produt Produttor tore e __# 1 Get __# __# 0 c 1 fals false e Put __# 1 c 0 true Produt Produttor tore e __# 1 Consum Consumato atore re __# 0 Get __# __# 0 c 1 fals false e Put __# 1 c 0 true Produt Produttor tore e __# 1 Consum Consumato atore re __# 0 Get __# __# 0 c 1 fals false e Put __# 1 c 0 true Produt Produttor tore e __# 1 Consum Consumato atore re __# 0 Get __# __# 0 c 1 fals false e Put __# 1 c 0 true Produt Produttor tore e __# 1 Consum Consumato atore re __# 0 Get __# __# 0 c 1 fals false e Put __# 1 c 0 true Produt Produttor tore e __# 1 Consum Consumato atore re __# 0 Get __# __# 0 c 1 fals false e Consum Consumato atore re __# 0 Put __# __# 1 c -1 true rue
22
Guido Massa Finoli Programmazione Java con Threads Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Get __# __# 0 c 0 fals false e Consum Consumato atore re __# 0 Produt Produttor tore e __# 1 Put __# __# 1 c -1 true rue Produt Produttor tore e __# 1 Get __# __# 0 c 1 fals false e Put __# 1 c 0 true Produt Produttor tore e __# 1 Consum Consumato atore re __# 0 Get __# __# 0 c 1 fals false e
Per evitare anomalie di questo tipo si trasformano put() e get() in metodi sincronizzati, ovvero si crea un Monitor e si utilizzano i metodi : wait(), notifyAll() , notify() Il monitor permette di aggiungere alla definizione di tipo di dati astratto una specifica della sincronizzazione fra i threads per mezzo dell’invocazione dei seguenti metodi: - wait(): tale metodo rilascia il lock (mutua esclusione) sull'oggetto e sospende il thread che lo invoca in attesa di una notifica. - notifyAll(): tale metodo risveglia tutti i thread sospesi sull'oggetto in attesa di notifica. I thread risvegliati competono per acquisire il lock (mutua esclusione) sull'oggetto. - notify(): tale metodo risveglia un thread scelto casualmente tra quelli sospesi sull'oggetto in attesa di notifica. Il thread risvegliato compete per acquisire il lock (mutua esclusione) sull'oggetto. I metodi wait(), notify() e notifyAll() devono essere invocati dall'interno di un metodo o blocco sincronizzato. Quindi nel nostro esempio le classi Produttore e Consumatoore rimangono identiche mentre cambiano i metodi get() e put().
23
Guido Massa Finoli Programmazione Java con Threads 7) Particolarità dei Threads
a) Caso class MyThread extends Thread
{ MyThread MyThread() () {} MyThread MyThread(Run (Runnabl nable e r) {super(r); (r); } public void run() { System.out.print("In .print("Insid side e Thr Thread ead "); } } class MyRunnable implements Runnable
{ public void run()
{ System.out.print(" .print(" Ins Inside ide Run Runnab nable" le"); ); } } class ProveG
{ main(String[] args) public static void main(String[] { new MyThread().start(); new MyThread(new MyRunnable()).start();
} } Inside Inside Thread Thread Inside Inside Thread Thread In ques questo to caso caso vien viene e crea creato to un prim primo o ogge oggett tto o MyTh MyThre read ad invo invoca cand ndo o il cost costru rutt ttor ore e MyTh MyThre read ad() (), , una una volt volta a crea creato to l’og l’ogge gett tto o con con lo star start( t() ) si atti attiva va il meto metodo do run( run() ) che scrive “Inside Thread”. Nel secondo caso viene invocato il secondo costr ostrut utto tore re che ha come ome param arame entro ntro un ogg oggetto etto Runn Runnab able le che vien viene e crea creato to al moment momento, o, il metodo metodo nel costru costrutto ttore re invoca invoca la runna runnable ble della della superc superclas lasse se ma essa essa non non vine vine mai mai util utiliz izza zata ta in quan quanto to al mome moment nto o dell dello o star start( t() ) si atti attiva va semp sempre re il run() run() del Threa Thread. d. Infatt Infatti i il codic codice e è equiva equivalen lente te al: class MyThread extends Thread
{ MyThread MyThread() () {} MyThread MyThread(Run (Runnabl nable e r) {} public void run() { System.out.print("In .print("Insid side e Thr Thread ead "); } } class MyRunnable implements Runnable
{ public void run()
{ System.out.print(" .print(" Ins Inside ide Run Runnab nable" le"); ); } } class ProveG
{ main(String[] args) public static void main(String[] { new MyThread().start(); new MyThread(new MyRunnable()).start();
24
Guido Massa Finoli Programmazione Java con Threads } }
b) Caso : esempi esempio o
NON sincro sincroniz nizzat zato o
class ProveG2 implements Runnable
{ int x, y; public void run()
{ for(int i = 0 ; i < 1 0 ; i + + )
{ x = 12 - i; y = 12 + i; System.out.println(x .println(x + " " + y + " "); try { Thread.sleep(100); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } } System.out.println(" .println(" Fine "); "); } main(String args[]) public static void main(String { Prov ProveG eG2 2 run run = new ProveG2(); Thre Thread ad t1 = new Thread(run); Thre Thread ad t2 = new Thread(run); t1.start(); t2.start(); } } 12 12 12 12 11 13 11 13 10 14 10 14 9 15 9 15 8 16 8 16 7 17 7 17 6 18 6 18 5 19 5 19 4 20 4 20 3 21 3 21 Fine Fine
25
Guido Massa Finoli Programmazione Java con Threads
c) Caso : esempi esempio o
intero intero metodo metodo run() run() sincro sincroniz nizzat zato o
class ProveG2 implements Runnable
{ int x, y; public synchroniz synchronized ed void run()
{ for(int i = 0 ; i < 1 0 ; i + + )
{ x = 12 - i; y = 12 + i; System.out.println(x .println(x + " " + y + " "); try { Thread.sleep(100); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } } System.out.println(" .println(" Fine "); "); } main(String args[]) public static void main(String { Prov ProveG eG2 2 run run = new ProveG2(); Thre Thread ad t1 = new Thread(run); Thre Thread ad t2 = new Thread(run); t1.start(); t2.start(); } } 12 12 11 13 10 14 9 15 8 16 7 17 6 18 5 19 4 20 3 21 Fine 12 12 11 13 10 14 9 15 8 16 7 17 6 18 5 19 4 20 3 21 Fine
26
Guido Massa Finoli Programmazione Java con Threads
d) Caso : sincro sincroniz nizzaz zazion ione e del blocco blocco La sempli semplice ce sincro sincroniz nizzaz zazion ione e del del blocco blocco non compor comporta ta alcun alcun cambia cambiamen mento to rispet rispetto to al prim primo o esem esempi pio, o, in quan quanto to il bloc blocco co del del cicl ciclo o for( for() ) vie viene eseg esegui uito to una una sola sola volta volta e poi rilasc rilasciat iato. o. class ProveG2 implements Runnable { int x, y; public void run() { for(int i = 0 ; i < 1 0 ; i + + ) synchronized (this )
{ x = 12 - i; y = 12 + i; System.out.println(x .println(x + " " + y + " "); try { Thread.sleep(100); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } } System.out.println(" .println(" Fine "); "); } 12 12 12 12 11 13 11 13 10 14 9 15 8 16 7 17 10 14 9 15 8 16 7 17 6 18 5 19 4 20 3 21 Fine 6 18 5 19 4 20 3 21 Fine
27
Guido Massa Finoli Programmazione Java con Threads 8) Esempio di Automa Cellulare utilizzando Threads A) Caso Classe AC_Ricorsive utilizza una ricorsiva ciclica per settare a 1 le cellule di una struttura cellulare 30 x 30. Classe StatoCellule reperisce lo stato di una cellula ( che può essere 0 o 1) e lo modifica sulla base dei valori delle 4 cellule adiacenti. Classe Thread definisce un ciclo infinito che dai valori delle 4 cellule adiacenti cambia quello della cellula di riferimento modificandone il colore a seconda che sia 0 o 1. Definizione di un Applet che crea tutti gli oggetti necessari e attiva un thread corrispondente ad ogni cellula.
ChiamaCell StatoCellula
Crea o etti
ThreadCellula
Attivazione AC_Ricorsive
/* Cla Classe sse pe per r def defini inire re una ric ricors orsiva iva mer meromo omorfa rfa pas passat sate e le co coord ordina inate te del pun punto to d’inne d’i nnesco sco e il num numero ero di cic cicli li det determ ermina inanti nti il cor corris rispon ponden dente te num numero ero di cel cellul lule e e loro coordinate caricate in schiere */ AC_Ricorsive ive { public class AC_Ricors valx valx[], [], valy valy[]; []; double valIx[], valIy[]; valIy[]; public int valIx[], px1, , px2 px2, , py1 py1, , py2 py2; ; int px1 x_max, x_min; x_min; public double x_max, ass_x_max, ass_x_min; ass_x_min; public int ass_x_max, ass_y_max, ass_y_min; ass_y_min; int ass_y_max, y_max, y_min; y_min; double y_max, n_ind; int n_ind; coeff_x, coeff_y; coeff_y; public double coeff_x, public AC_Ricorsive(int x1, int x2, int y1, int y2, int h){ valx = new dou double ble[h]; valIx = new new in int t [h]; valy = new dou double ble[h]; valIy = new new in int t [h];
px1 = px2 = py1 = py2 = x_max
x1; x2; y1; y2; = 0;
28
Guido Massa Finoli Programmazione Java con Threads x_min = y_max = y_min = n_ind =
1000 1000; ; 0; 1000 1000; ; h;
}
c4) { public void Calco_1(double c1, double c2, double c3, double c4) valx[0]= valx[0] = c1; valx[1] valx [1]= = c2; valy[0]=c3; valy [0]=c3; valy[1]=c4; valy [1]=c4; valIx[0 valIx [0]= ]= (int) valx[0]; valx[0]; valIx[1 valIx [1]= ]= (int) valx[1]; valx[1]; valIy[0 valIy [0]= ]= (int) valy[0]; valy[0]; valIy[1 valIy [1]= ]= (int) valy[1]; valy[1]; int k; for( k = 2 double d1 double d2 double h1 double h2
; k
(valx valx[k]> [k]>x_max x_max) ) x_max (valx valx[k]< [k] [k]>y_max y_max) ) y_max (valy valy[k]< [k]
= valx[k]; valx[k]; = valx[k]; valx[k]; = valy[k]; valy[k]; = valy[k]; valy[k];
} ass_x_min =Math.abs((int) x_min); x_min); ass_x_max =Math.abs((int) x_max); x_max); coeff_x =50.00/(ass_x_min =50.00/(ass_x_min + ass_x_max); ass_x_max); ass_y_min =Math.abs((int) y_min); y_min); ass_y_max =Math.abs((int) y_max); y_max); coeff_y =50.00/(ass_y_min =50.00/(ass_y_min + ass_y_max); ass_y_max);
/* Cl Clas asse se ch che e de defi fini nisc sce e lo st stat ato o di un una a si sing ngol ola a ce cell llul ula a * ind indivi ividua duata ta dal dalle le sue coo coordi rdinat nate e * Il cos costru trutt ttore ore set setta ta lo sta stato to ind indizi iziale ale * Met Metodo odo Inq Inquir uiryCe yCell ll int interr erroga oga lo sta stato to * Met Metodo odo Mod ModSta Stato to mod modifi ifica ca lo sta stato to */ StatoCellule ule { public class StatoCell CBx, , CBy CBy; ; /* coo coordi rdinat nate e */ int CBx SCBi; /* sta stato to ini inizi ziale ale */ int SCBi; SCBf; /* st stat ato o fi fina nale le */ int SCBf; StatoCellule(int x, int y){
29
Guido Massa Finoli Programmazione Java con Threads CBx = x; CBy = y; SCBi = 0; SCBf = 1; } StatoCellule(int x, int y, int Si){ CBx = x; CBy = y; SCBi = Si; Si; SCBf = 9; } StatoCellule(int x, int y, int Si, int Sf){ CBx = x; CBy = y; SCBi = Si; SCBf = Sf; } public synchroniz synchronized ed int InquiryCell(int x, int y, char St){ (St == 'i' 'i') )return SCBi; SCBi; if(St SCBf; else retu return rn SCBf; } public synchroniz synchronized ed int InquiryCell(char St){ (St == 'i' 'i') )return SCBi; SCBi; if(St SCBf; else retu return rn SCBf; } public synchroniz synchronized ed void ModStato(int x0, int x1, int x2,int x3, int x4){ M0, M1, M1, M2, M2, M3, M3, M4; M4; int M0, CCS0, CCST; CCST; int CCS0, M0 = x0; M1 = x1; M2 = x2; M3 = x3; M4 = x4; CCST = M1 + M2 + M3 + M4; /* Se le 4 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* Al Allo lora ra st stat ato o fi fina nale le a 0 */ (CCST= T==4 =4) ) {SCBf = 0; if(CCS } /* Se le 3 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* Al Allo lora ra st stat ato o fi fina nale le a 1* 1*/ / if(CCST==3) SCBf = 1; /* Se le 2 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* Al Allo lora ra st stat ato o fi fina nale le a 0* 0*/ / if(CCST==2) SCBf = 0; /* Se le 1 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* Al Allo lora ra st stat ato o fi fina nale le a 0* 0*/ / if(CCST==1) SCBf = 1; /* Se 0 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* Al Allo lora ra st stat ato o fi fina nale le a 0* 0*/ / if (CCST==0) SCBf = 0; SCBi = SCBf; SCBf; } }
30
Guido Massa Finoli Programmazione Java con Threads /* * * * * *
Thread di att Thread attiva ivazio zione ne del delle le cel cellul lula a Passat Pas sati i 4 rif riferi erimen menti ti del delle le cel cellul lule e in intor torno no a que quella lla di rif riferi erimen mento to Passat Pas sato o il bo botto ttone, ne, e le coo coordi rdinat nate e de della lla cel cellul lula a di rif riferi erimen mento to Il ci cicl clo o ru run n mo modi difi fica ca lo st stat ato o de dell lla a ce cell llul ulla la di ri rif f su sull lla a ba base se di qu quel ello lo dell de lle e al altr tre e 4 ce cell llul ule e e ca camb mbia ia il co colo lore re de del l bo bott tton one e di ri rif. f. */
Thread { public class ThreadCellule extends Thread CBx, , CBy CBy; ; /* coo coordi rdinat nate e */ int CBx /* sta stato to ini inizi ziale ale */ int m, q; SCBi, SCBf; SCBf; /* st stat ato o fi fina nale le */ int SCBi, StatoCellule op0 op0, , op1 op1, , op2 op2, , op3 op3, , op4 op4; ; ACS0, ACS1, ACS1, ACS2, ACS2, ACS3, ACS3, ACS4; ACS4; int ACS0, Button ok ok; ; long l; ThreadCellule(StatoCellule oCellule pp0,StatoCellule pp0,StatoCellule pp1,StatoCellule pp1,StatoCellule pp2, public ThreadCellule(Stat StatoCel StatoCellule lule pp3,Stat pp3,StatoCel oCellule lule pp4,Butt pp4,Button on kk, int x , int y){ CBx = x; CBy = y; op0 = pp0; pp0; op1 = pp1; pp1; op2 = pp2; pp2; op3 = pp3; pp3; op4 = pp4; pp4; ok = kk;
} public void run(){ long q = 200; ;){ for(; ;){
ACS0 = op0 op0.InquiryCell( .InquiryCell('i' 'i'); ); ACS1 = op1 op1.InquiryCell( .InquiryCell('i' 'i'); ); ACS2 = op2 op2.InquiryCell( .InquiryCell('i' 'i'); ); ACS3 = op3 op3.InquiryCell( .InquiryCell('i' 'i'); ); ACS4 = op4 op4.InquiryCell( .InquiryCell('i' 'i'); ); op0.ModStato( op0 .ModStato(ACS0 ACS0, , ACS1, ACS1, ACS2, ACS2, ACS3, ACS3, ACS4); ACS4); System.out.println("S .println("S " +ACS0 + " " + CBx + CBy + " " + ACS1 + ACS2 + ACS3 + ACS4); ACS4); ok.setLabel( ok .setLabel(" " " + ACS0); ACS0); 0){ if(ACS0 == 0){ ok.setBackground(Color. ok .setBackground(Color. green); } 1){ if(ACS0 == 1){ ok.setBackground(Color. ok .setBackground(Color. blue ); } try {
Thread.sleep(q); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } } } } /*
31
Guido Massa Finoli Programmazione Java con Threads */ /* 1) cre creazi azion one e bot botton toni i in sch schier iera a bidime bidimensi nsiona onale le * 2) cre creazi azion one e cel cellul lula a in schier schiera a bid bidime imensi nsiona onale le * 3) creazion creazione e thr thread ead in schiera schiera bidimen bidimensio sional nale e */ public class ChiamaCell extends Applet{ 31; public static final int n = 31; = 0; static sta tic int ind Button bList[] bList[][] [] = new Button[n][n]; Col1[][] [] = new int Col1[] new in int t [n][n]; StatoCellule nome[] nome[][] [] = new StatoCellule[n][n];
StatoCellule pp0 pp0, ,pp1 pp1, ,pp2 pp2, ,pp3 pp3, ,pp4 pp4; ; ThreadCellule TH TH[] [][] [] = new ThreadCellule[n][n]; public void init(){ AC_Ricor AC_Ricorsive sive Eseguiri Eseguiric c = new AC_Ric AC_Ricors orsive ive(-1 (-1, , 1, 1, 1, 8100); 8100); Eseguiri Eseguiric.Ca c.Calco_ lco_1(50 1(50, , 166, 100, 100); 100); q<8000 00; ; q++) q++){ { for (int q = 1; q<80 ((Eseguiric.valIx[q [q] ] <(n-1)) -1)) & (Esegu (Eseguiri iric. c.valIy valIy[q]<( [q]<(n-1))){ if((Eseguiric.valIx Col1[Eseguiric. Col1 [Eseguiric.valIy valIy[q]][Eseguiric. [q]][Eseguiric.valIx valIx[q]]=1;} [q]]=1;} } i++){ { for (int i = 1 ; i < n; i++) j++){ { for (int j = 1 ; j < n; j++) Col1[i][j]==1){ [i][j]==1){ if(Col1 nome[i][ nome [i][j] j] = (StatoCe (StatoCellul llule) e) new StatoCel StatoCellule lule(i, (i, j, 1);} else
nome[i][ nome [i][j] j] = (StatoCe (StatoCellul llule) e) new StatoCel StatoCellule lule(i, (i, j, 0); Col1[i] Col1 [i][j] [j] = nome[i][j].Inq nome[i][j].InquiryCell(i, uiryCell(i, j, 'i' 'i'); ); } } setLayout( new GridLayout(n,n)); setFont(new Font("Helvetica" Font("Helvetica", , Font Font. .BOLD , 12)); 12)); resize(1000,800); i++){ { for(int i = 1; i < n; i++) j++){ { for(int j = 1 ; j < n; j++) Col1[i][j]; int k = Col1[i][j]; bList[i] bList [i][j] [j] = (Butt (Button) on) add( add(new Button(" Button(" " + k)); k)); if(k == 0) bList[i][j].setBackground(Color. bList [i][j].setBackground(Color. white); (k== 1) if(k== bList[i][j].setBackground(Color. bList [i][j].setBackground(Color. yellow ); ); } } } /* bot botton tone e pre premu muto to e att attiva ivazio zione ne Thr Thread ead*/ */ keyDown(Event evtObj, int key){ public boolean keyDown(Event
==evtObj. .F5){ if(key ==evtObj for(int i = 1 ; i
pp0 = nome[i][j]; nome[i][j]; int M x = i - 1 ; if(Mx<1)Mx= n-1; int M y = j ;
32
Guido Massa Finoli Programmazione Java con Threads pp1 = nome[Mx][My]; nome[Mx][My]; My = j -1; if(My<1)My= n-1; Mx = i; pp2 = nome[Mx][My]; nome[Mx][My]; Mx = i +1; -1)Mx= 1; if(Mx>n-1)Mx= My = j; pp3 = nome[Mx][My]; nome[Mx][My]; My = j +1; -1)My= 1; if(My> n-1)My= Mx = i; pp4 = nome[Mx][My]; nome[Mx][My]; TH[i][ TH [i][j] j] = (ThreadC (ThreadCellu ellule2) le2) new ThreadCellule2(pp0 ThreadCellule2(pp0, , pp1 pp1, , pp2, pp2 , pp3 pp3, , pp4 pp4, , bList[i][ bList[i][j],i j],i, , j); TH[i][j].start(); TH [i][j].start(); } } return retu rn true; } return retu rn fals false e; } public void stop(){
} public void destroy(){
} }
33
Guido Massa Finoli Programmazione Java con Threads
34
Guido Massa Finoli Programmazione Java con Threads
B) Caso In questo caso viene definito anche uno stato del sistema che controlla il numero di celle con stato attivo (= 1)unato ChiamaCell2 StatoCellula2
Crea o etti
ThreadCellula2
Attivazione yield() AC_Ricorsive
StatoSistema
Stato Sistema StatoSistema ema { public class StatoSist num; ; /* Nu Nume mero ro di el elem emen enti ti at atti tivi vi int num public static int del; private boolean F = false; tot; ; private int tot
*/
StatoSistema(int att){ num = att; att; 55; del = 55; }
public synchronized synchronized int CallTH(int s0, int som){ val = s0; s0; int val
tot = som; som; num) ) { if(del >= num if (s0 == 1) { del = del - 1; val val = 0;} System.out.println("U .println("Usc sc Ma Magg gg. . " + del ); } else else if (del <=0){ if (s0 == 0) { del = del + 1; val val = 1;} System.out.println("U .println("Usc sc Mi Mino nore re " + del ); } else { if ( tot = = 4 ) {
35
Guido Massa Finoli Programmazione Java con Threads if (s0 == 1) {
del = del - 1; val = 0; System.out.println(" .println(" - - 1 " + del ); }
} 3){ else el se if (tot = = 2 | | tot == 3){ (s0 == 0) { if del = del + 1; val = 1; System.out.println(" .println(" + 1 " + del ); }
} 1){ else el se if (tot = = 0 | tot == 1){ val = s0; s0; } } return val;
} }
StatoCellula2 chiama StatoSistema per cambiare o meno lo stato della cellula: StatoCellule2 ule2 { public class StatoCell CBx, , CBy CBy; ; /* coo coordi rdinat nate e */ int CBx SCBi; /* sta stato to ini inizi ziale ale */ int SCBi; SCBf; /* st stat ato o fi fina nale le */ int SCBf; StatoSistema SS SS; ; StatoCellule2( int x, int y, StatoSis StatoSistema tema sis){ sis){ CBx = x; CBy = y; SCBi = 0; SCBf = 1; SS = sis; sis; } StatoCellule2( int x, int y, int Si, StatoSis StatoSistema tema sis){ sis){ CBx = x; CBy = y; SCBi = Si; Si; SCBf = 9; SS = sis; sis; } StatoCellule2( int x, int y, int Si, int Sf, StatoSis StatoSistema tema sis){ sis){ CBx = x; CBy = y; SCBi = Si; SCBf = Sf; SS = sis; sis; } public synchroniz synchronized ed int InquiryCell(int x, int y, char St){ (St == 'i' 'i') )return SCBi; SCBi; if(St SCBf; else retu return rn SCBf; }
36
Guido Massa Finoli Programmazione Java con Threads public synchroniz synchronized ed int InquiryCell(char St){ (St (S t == 'i') 'i' )return SCBi; SCBi; if SCBf; else retu return rn SCBf;
} public synchroniz synchronized ed void ModStato(int x0, int x1, int x2,int x3, int x4){ M0, M1, M1, M2, M2, M3, M3, M4; M4; int M0, CCS0, CCST; CCST; int CCS0,
M0 = x0; M1 = x1; M2 = x2; M3 = x3; M4 = x4; CCST = M1 + M2 + M3 + M4; /*Sy /* Syst stem em.o .out ut.p .pri rint ntln ln(" ("I I " +M +M0 0 + " " + M1 + M2 + M3 + M4 M4); );*/ */ /* Se le 4 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* Al Allo lora ra st stat ato o fi fina nale le a 0 */ (CCST= T==4 =4) ) {SCBf = SS SS.Cal .CallTH( lTH(M0, M0, CCST); CCST); if(CCS } /* Se le 3 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* SS.CallTH(M0, .CallTH(M0, CCST); if(CCST==3) SCBf = SS /* Se le 2 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* SS.CallTH(M0, .CallTH(M0, CCST); if(CCST==2) SCBf = SS /* Se le 1 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* SS.CallTH(M0, .CallTH(M0, CCST); if(CCST==1) SCBf = SS /* Se 0 ce cell lle e in into torn rno o al alla la ce cell llul ula a di ri rif f so sono no a st stat ato o 1 */ /* SS.CallTH(M0, .CallTH(M0, CCST); if (CCST==0) SCBf = SS SCBi = SCBf; SCBf; } }
Il Thread viene eseguito in un unico blocco sincronizzato ed inoltre viene invocato il metodo yield() per permettere agli altri Th runnable di passare a stato di run() Thread { public class ThreadCellule2 extends Thread CBx, , CBy CBy; ; /* coo coordi rdinat nate e */ int CBx /* sta stato to ini inizi ziale ale */ int m, q; SCBi, SCBf; SCBf; /* st stat ato o fi fina nale le */ int SCBi, StatoCellule2 op0 op0, , op1 op1, , op2 op2, , op3 op3, , op4 op4; ; ACS0, ACS1, ACS1, ACS2, ACS2, ACS3, ACS3, ACS4, ACS4, ACSW; ACSW; int ACS0, Button ok ok; ; long l; ThreadCellule2(StatoCellule2 le2 pp0,StatoCellule2 pp0,StatoCellule2 pp1,StatoCellule2 pp1,StatoCellule2 public ThreadCellule2(StatoCellu pp2, CBx = CBy = op0 = op1 = op2 = op3 = op4 =
StatoCel StatoCellule lule2 2 pp3,Stat pp3,StatoCel oCellule lule2 2 pp4,Butt pp4,Button on kk, int x , int y){ x; y; pp0; pp0; pp1; pp1; pp2; pp2; pp3; pp3; pp4; pp4;
37
Guido Massa Finoli Programmazione Java con Threads ok = kk;
} public void run(){ long q = 150; ;){ for(; ;){ synchronized (this){
ACS0 = op0 op0.InquiryCell( .InquiryCell('i' 'i'); ); ACSW = ACS0; ACS0; ACS1 = op1 op1.InquiryCell( .InquiryCell('i' 'i'); ); ACS2 = op2 op2.InquiryCell( .InquiryCell('i' 'i'); ); ACS3 = op3 op3.InquiryCell( .InquiryCell('i' 'i'); ); ACS4 = op4 op4.InquiryCell( .InquiryCell('i' 'i'); ); System.out.println("S_ .println("S_IN IN " +ACS0 + " " + CBx + " " + CBy + " " + ACS1 + ACS2 + ACS3 + ACS4); ACS4); op0.ModStato( op0 .ModStato(ACS0 ACS0, , ACS1, ACS1, ACS2, ACS2, ACS3, ACS3, ACS4); ACS4); ACS0 = op0 op0.InquiryCell( .InquiryCell('i' 'i'); ); ok.setLabel( ok .setLabel(" " " + StatoSis StatoSistema tema. .del); 0){ if(ACS0 == 0){ ok.setBackground(Color. ok .setBackground(Color.green); } 1){ if(ACS0 == 1){ ok.setBackground(Color. ok .setBackground(Color.blue); } System.out.println("S_ .println("S_OUT OUT " +ACS0 + " " + CBx + " " + CBy + " " + ACS1 + ACS2 + ACS3 + ACS4); ACS4); } (); yield (); } } }
ChiamaCellule2 e AC_Ricorsive rimangono allo stesso modo della precedente versione.
38
Guido Massa Finoli Programmazione Java con Threads
39
Guido Massa Finoli Programmazione Java con Threads Gruppi di Threads : Threads Deamon e User Ogni thread Java è un membro di un thread group. Il Thread group fornisce un meccanismo per collezionare più thread in un singolo oggetto e manipolare questi threads tutti insieme piuttosto che individualmente I Java thread groups sono implementati dalla classe java.lang.ThreadGroup. java.lang.ThreadGroup. Il runtime system pone un thread in un thread group durante la sua costruzione. Quando viene creato un thread, si può permettere al runtime system di porlo in un default group o si può esplicitamente selezionare il gruppo desiderato. Il thread è un membro permanente del gruppo in cui è inserito all’atto della creazione; non è quindi possibile spostare un thread in un nuovo gruppo dopo la sua creazione . Thread main
Daemon = termina con in main
ThreadGroup GR
Threads
User = prosegue autonomamente
Se vengono generati solo thread daemon, opzione NON di default, l’applicazione termina quando termina il primo thread di partenza (quello che esegue il main); in quel momento, anche tutti i thread demoni sono stoppati. Se si desidera questo comportamento, devono quindi essere marcati esplicitamente come demoni tutti i thread generati Se esiste invece almeno un thread utente, l’applicazione non termina con il thread del main, ma solo quando tutti i thread utente saranno terminati; al completamento dell’ultimo thread utente, anche tutti gli eventuali thread demoni saranno arrestati e l’applicazione potrà avere fine I thread utente sono quindi dotati di maggiore indipendenza rispetto ai demoni, e permettono ad un’applicazione di avere una vita più lunga rispetto al thread del solo main. Questa situazione potrebbe in alcuni casi non essere desiderabile; esiste sempre la possibilità di invocare il metodo exit di System.
40
Guido Massa Finoli Programmazione Java con Threads 9) Simulazione sistema automatico di produzione Videata_1 = Crea oggetti e attiva TH
ChiamaFrame = JFrame x bottoni e videata
TH inizio Thread CS
Coda
Oggetti B Oggetti A Magazzino Ma
Java.swing Java.awt
Magazzino Mb
MagazzinoScambio
Creazione altri oggetti come CS
Thread SK
TH Fine
41
Guido Massa Finoli Programmazione Java con Threads Main public class Videata_1
{ public public public public public
2000, , sTSK = 1000 1000, , sMAP = 100; 100; static int sTCS = 2000 300, sMAS = 200; 200; static int sMAC = 300, 41, b1 = 11; 11; static int b2 = 41, static Coda DD ; main(String args[]) static void main(String
{ DD = new Coda(); Chiama ChiamaFra Frame me pp = new ChiamaFrame(); Thread ThreadGro Group up GRCS GRCS = new ThreadGroup("OggCS" ThreadGroup("OggCS"); ); Thread ThreadGro Group up GRSK GRSK = new ThreadGroup("OggSK" ThreadGroup("OggSK"); );
/* Cr Creo eo 1 Og Ogge gett tto o Ma Maga gazz zzin ino o */ MagSca MagScambi mbio o Maga Maga = new MagScambio(); Magazz Magazzino ino Ma = new Magazzino(); Magazz Magazzino ino Mb = new Magazzino(); /* Cr Creo eo 3 og ogge gett tti i de dell lla a Cl Clas asse se_A _A */ Clas Classe se_A _A A1 = new Classe_A(Ma); Clas Classe se_A _A A2 = new Classe_A(Ma); Clas Classe se_A _A A3 = new Classe_A(Ma); /* Cr Creo eo 3 og ogge gett tti i de dell lla a Cl Clas asse se_B _B */ Clas Classe se_B _B B1 = new Classe_B(Mb); Clas Classe se_B _B B2 = new Classe_B(Mb); /* Cr Creo eo 10 Th Thre read ad */ Thread ThreadCS CS TH[] TH[] = new ThreadCS[b2]; Thread ThreadSK SK TK[] TK[] = new ThreadSK[b1]; JButto JButton n BO[] BO[] = new JButton[7];
/*
for fo r (int (int i=1 =1; ; i< i<11 11; ;i+ i++) +){ { TH[i].start(); System.o Syst em.out.p ut.print rintln(" ln("Imme Immesso sso " + i); }*/
pp.A1 pp.A1.setBackground(Color. .setBackground(Color. GREEN ); ); pp.A1 pp.A1.setText( .setText(" "A1 " + "POS "POS " + 10+ 10+ " pp.A2 pp.A2.setBackground(Color. .setBackground(Color. GREEN ); ); pp.A2 pp.A2.setText( .setText(" "A2 " + "POS "POS " + 10+ 10+ " pp.A3 pp.A3.setBackground(Color. .setBackground(Color. GREEN ); ); pp.A3 pp.A3.setText( .setText(" "A3 " + "POS "POS " + 10+ 10+ " pp.B1 pp.B1.setBackground(Color. .setBackground(Color. ORANGE ); ); pp.B1 pp.B1.setText( .setText(" "B1 " + "VAL "VAL " + 10); 10); pp.B2 pp.B2.setBackground(Color. .setBackground(Color. orange ); pp.B2 pp.B2.setText( .setText(" "B2 " + "VAL "VAL " + 10); 10); pp.MA pp.MA.setBackground(Color. .setBackground(Color. MAGENTA ); ); pp.MA pp.MA.setText( .setText(" "MA " + "VAL "VAL " + 0); 0); BO[1 BO[1] ] = pp. pp.A1 A1; ; BO[2 BO[2] ] = pp. pp.A2 A2; ; BO[3 BO[3] ] = pp. pp.A3 A3; ; BO[4 BO[4] ] = pp. pp.B1 B1; ; BO[5 BO[5] ] = pp. pp.B2 B2; ; BO[6 BO[6] ] = pp. pp.MA MA; ;
NEG " + 1 ) ; NEG " + 1 ) ; NEG " + 1 ) ;
i=1; i
42
Guido Massa Finoli Programmazione Java con Threads TH[i] TH[i] = (Threa (ThreadCS dCS) ) new ThreadCS(GRCS, ThreadCS(GRCS, A1,A2,A3,B1,B2, A1,A2,A3,B1,B2, Maga, pp.N pp.N[i], [i], i, BO); BO); } i=1; i
Creazione videata con Jframe e Jbutton public class ChiamaFrame extends JFrame{
11; public static int a2 = 41, a1 = 11; JPanel nord = new JPanel(); JPanel centro = new JPanel(); JPanel sud = new JPanel(); JButton N[] = new JButton[a2]; JButton Q[] = new JButton[a1]; JButton A1 = new JButton("Car JButton("Carico ico A1" A1"); ); JButton A2 = new JButton("Car JButton("Carico ico A2" A2"); ); JButton A3 = new JButton("Car JButton("Carico ico A3" A3"); ); JButton B1 = new JButton("Car JButton("Carico ico B1" B1"); ); JButton B2 = new JButton("Car JButton("Carico ico B2" B2"); ); JButton MA = new JButton("Mag JButton("Maga a Scam Scambio" bio"); ); Ascoltatore listener = new Ascoltatore(); public ChiamaFrame()
{ "Ciclo_Thread"); ); super("Ciclo_Thread" for (int h = 1 ; h
Q[h].setSize(150, [h].setSize(150, 150); } Cont Contai aine ner r c = this .getContentPane(); /*centro.setLayout(new /*centro.se tLayout(new FlowLayout( FlowLayout());*/ ));*/ centro.setLayout( centro .setLayout(new GridLayout(5,1)); h++){ { for (int h = 1 ; h < a2; h++) centro.add( centro .add(N N[h], CENTER_ALIGNMENT ); ); } h++){ { for (int h = 1 ; h < a1; h++) centro.add( centro .add(Q Q[h], CENTER_ALIGNMENT ); );
43
Guido Massa Finoli Programmazione Java con Threads } /*nord.setLayout(new /*nord.setL ayout(new FlowLayout( FlowLayout());*/ ));*/ nord.setLayout( nord .setLayout(new GridLayout(1,2)); nord.add( nord .add(A1 A1, , LEFT_ALIGNMENT ); ); nord.add( nord .add(A2 A2, , LEFT_ALIGNMENT ); nord.add( nord .add(A3 A3, , LEFT_ALIGNMENT ); sud.setLayout( sud .setLayout( new GridLayout(1,2)); sud.add( sud .add(B1 B1, , LEFT_ALIGNMENT ); ); sud.add( sud .add(B2 B2, , LEFT_ALIGNMENT ); ); sud.add( sud .add(MA MA, , LEFT_ALIGNMENT ); ); c.setLayout( new BorderLayout()); c.add(nord c.add(nord, , BorderLa BorderLayout yout. .NORTH ); ); c.add(centro c.add(centro, , BorderLa BorderLayout yout. .CENTER ); c.add(sud c.add(sud, , BorderLa BorderLayout yout. .SOUTH ); ); MA.addActionListener( MA .addActionListener(listener listener); ); setSize(1000,700); setVisible(true); }
public class Ascoltatore implements ActionListener
{ actionPerformed(ActionEvent Event e){ public void actionPerformed(Action Object Object src = e.getS e.getSour ource( ce(); ); (src==MA) ) if (src==MA {A1 A1.setBackground(Color. .setBackground(Color. BLACK ); ); } } }
Creazione ciclo del TH CS Thread { public class ThreadCS extends Thread conta; int m, q, conta; Classe_A op1, op1, op2 op2, , op3 op3; ; Classe_B ob1 ob1, , ob2 ob2; ; MagScambio MM MM; ; JButton ok ok; ; JButton OB OB[]; []; long l; ThreadInizio ini ini; ; ThreadFine fin fin; ; ThreadCS(Th S(Thread readGrou Group p cs, Classe_A Classe_A pp1, Classe_A Classe_A pp2, Classe_A Classe_A pp3, public ThreadC Classe Classe_B _B bb1, bb1, Classe Classe_B _B bb2, bb2, MagSca MagScambi mbio o MA, JButto JButton n kk , int u, JButton JButton BB[]){ BB[]){ q = u; conta = 0; op1 = pp1; pp1; op2 = pp2; pp2; op3 = pp3; pp3; ob1 = bb1; bb1; ob2 = bb2; bb2; MM = MA; ok = kk; m = 1;
44
Guido Massa Finoli Programmazione Java con Threads OB = BB; ini = new ThreadInizio(q ThreadInizio(q, false); fin = new ThreadFine(q ThreadFine(q, false); } public void run(){
m = 1; for(int k = 1 ; k < 5 ; k + + ) { k = k + 1; 1){ if (m == 1){ ini. ini .G = true; System.out.println("In .println("Inizi izio o " + q); ThreadCS.this.setPriority(8); } <10) {m = op1 op1.CaricaA( .CaricaA(m m, 0); if (m<10) OB[1].setBackground(Color. OB [1].setBackground(Color. WHITE ); ); OB[1].setText( OB [1].setText("A "A1 1 " + " VA VAL " + op1 op1. .POS + " " + op1 op1. .NEG NEG); ); ok.setBackground(Color. ok .setBackground(Color.GREEN ); ); ok.setText( ok .setText("T" "T" + q + " V " + m); /*Sy /* Syst stem em.o .out ut.p .pri rint ntln ln(" ("OP OP1 1 Ca Cari rico co " + m + " " + q) q);* ;*/ / } <10) {m = op2 op2.CaricaA( .CaricaA(m m, 0); if (m<10) OB[2].setBackground(Color. WHITE ); OB[2].setBackground(Color. ); OB[2].setText( OB [2].setText("A "A2 2 " + " VA VAL " + op2 op2. .POS + " " + op2 op2. .NEG NEG); ); ok.setBackground(Color. ok .setBackground(Color.GREEN ); ); ok.setText( ok .setText("T" "T" + q + " V " + m);
} <10) {m = op3 op3.CaricaA( .CaricaA(m m, 0); if (m<10) OB[3].setBackground(Color. WHITE ); OB[3].setBackground(Color. ); OB[3].setText( OB [3].setText("A "A3 3 " + " VA VAL " + op3 op3. .POS + " " + op3 op3. .NEG NEG); ); ok.setBackground(Color. ok .setBackground(Color.GREEN ); ); ok.setText( ok .setText("T" "T" + q + " V " + m);
} try {
Thread.sleep(Video.Videata_1. sTCS ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } 10){ if (m>= 10){ for(int f=1;f<10;f++){ <300) ) {m = ob1 ob1.CaricaB( .CaricaB(m m, 0); 0); if (m<300 OB[4].setBackground(Color. OB [4].setBackground(Color. WHITE ); ); OB[4].setText( OB [4].setText("B "B1 1 " + " VA VAL " + ob1 ob1. .BOS ok.setBackground(Color. ok .setBackground(Color.ORANGE ); ); ok.setText( ok .setText("T" "T" + q + " V " + m); /*Sy /* Syst stem em.o .out ut.p .pri rint ntln ln(" ("OB OB1 1 Ca Cari rico co " + m + } <500) ) {m = ob2 ob2.CaricaB( .CaricaB(m m, 0); 0); if (m<500 OB[5].setBackground(Color. OB [5].setBackground(Color. WHITE ); ); OB[5].setText( OB [5].setText("B "B2 2 " + " VA VAL " + ob2 ob2. .BOS ok.setBackground(Color. ok .setBackground(Color.ORANGE ); ); ok.setText( ok .setText("T" "T" + q + " V " + m); /*Sy /* Syst stem em.o .out ut.p .pri rint ntln ln(" ("OB OB2 2 Ca Cari rico co " + m + /*ok.setBackground(Color.GRAY); ok.setText("T " + q + " VAL " + m);*/ /*Sy /* Syst stem em.o .out ut.p .pri rint ntln ln(" ("To Tota tale le " + m + " "
);
" " + q) q);} ;}*/ */
);
" " + q) q);} ;}*/ */
+ q) q);} ;}*/ */
45
Guido Massa Finoli Programmazione Java con Threads /*m = 50 /*m 501; 1;*/ */ } >499) ) { if (m>499 ok.setBackground(Color.BLUE ); ok.setBackground(Color. ); ok.setText( ok .setText("F" "F" + q + " V " + m); System.out.println("Fi .println("Final nale e " + m + " " + q); OB[6].setBackground(Color. GRAY ); OB[6].setBackground(Color. ); m = MM MM.put( .put(m m); OB[6].setText( OB [6].setText("M "MA A " + " VA VAL " + MM. MM.QUA QUA); ); } 0){ if (m == 0){ fin. fin .G=true; ThreadCS.this .setPriority(2); conta = conta + 1; ok.setBackground(Color. ok .setBackground(Color.WHITE ); ); ok.setText( ok .setText("F" "F" + q + " V " + m + " " + conta); conta); try {
Thread.sleep(Video.Videata_1. sTCS ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } m = 1; if ( k > 5 ) { ok.setBackground(Color. ok .setBackground(Color. BLACK ); ); ok.setText( ok .setText("F" "F" + q + " V " + m + " " + conta); conta); } }
} } } } }
Creazione ciclo del TH SK (vuoto) Thread { public class ThreadSK extends Thread int m, w, q;
Classe_A op1 op1, , op2 op2, , op3 op3; ; Classe_B ob1 ob1, , ob2 ob2; ; MagScambio MM MM; ; JButton ok ok; ; JButton OB OB[]; []; long l; ThreadSK(Th K(Thread readGrou Group p sk, Classe_A Classe_A pp1, Classe_A Classe_A pp2, Classe_A Classe_A pp3, public ThreadS Classe Classe_B _B bb1, bb1, Classe Classe_B _B bb2, bb2, MagSca MagScambi mbio o MA, JButto JButton n kk , int u, JButton JButton BB[]){ BB[]){ q = u; op1 = pp1; pp1; op2 = pp2; pp2;
46
Guido Massa Finoli Programmazione Java con Threads op3 = pp3; pp3; ob1 = bb1; bb1; ob2 = bb2; bb2; MM = MA; ok = kk; m = 1; OB = BB; } public void run(){ for(;;){
w = MM MM.get(); .get(); m = m + w; if (m >=5){ ok.setBackground(Color. ok .setBackground(Color. GRAY ); ); ok.setText( ok .setText("W" "W" + q + " V " + m); OB[6].setBackground(Color. OB [6].setBackground(Color. GRAY ); ); OB[6].setText( OB [6].setText(" "MA " + " VA VAL " + MM MM. .QUA QUA); ); try { Thread.sleep(Video.Videata_1.sTSK ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } m = 0; ok.setBackground(Color. ok .setBackground(Color. LIGHT_GRAY ); ok.setText( ok .setText("W" "W" + q + " V " + m); } } } }
TH inizio con put sulla Coda del Thread Thread d { public class ThreadInizio extends Threa int m, w, q; boolean G; long l; public ThreadInizio(int v, boolean f){
G = f; q = v; this.start(); } public void run(){ for(;;){ if (G == true){
Video.Videata_1. DD .put(q .put(q); G = false; } else { try {
sleep(5);}
(InterruptedException n e){ catch (InterruptedExceptio } } } }
47
Guido Massa Finoli Programmazione Java con Threads }
TH fine con get sulla Coda Thread Thread { public class ThreadFine extends Thread int m, w, q; boolean G; long l; public ThreadFine(int v, boolean f){
G = f; q = v; this.start(); } public void run(){ for(;;){ if (G == true){
m =Video.Videata_1.DD .get(); .get(); G = false; } else { try { sleep(5);} (InterruptedException n e){ catch (InterruptedExceptio } } } } }
Coda con put e get sincronizzati ed esclusivi Coda { public class Coda contenuto; private int contenuto; private boolean F = false; get() { public synchroniz synchronized ed int get() while(F == false){ try { wait(); }catch (InterruptedException (InterruptedException e){ } } F = false; System.out.println("Fi .println("Fine ne Thr Thread ead "+ contenuto); contenuto); notifyAll(); contenuto; return contenuto; } valore) ) { public synchroniz synchronized ed void put(int valore while(F == true ){ try { wait(); }catch (InterruptedException (InterruptedException e){ } }
48
Guido Massa Finoli Programmazione Java con Threads contenuto = valore valore; ; System.out.println("In .println("Inizi izio o Thr Thread ead "+ contenuto); contenuto); F = true; notifyAll(); } }
Oggetti A Classe_A A { public class Classe_ /* St Stat ato o dell della a Cl Clas asse se */ boolean F; POS; ; /* val valori ori pos positi itivi vi */ int POS NEG; ; int NEG Magazzino mv mv; ; za; za ; int Classe_A(Magazzino pp){ public Classe_A(Magazzino mv = pp; POS = 10; 10; NEG = 1; F = false ; } public Classe_A(int po, int ne, boolean q){
POS = po; po; NEG = ne; ne; F = q; } flag) { public int CaricaA(int m, int flag) rit, val; val; int k, rit, rit = 0; val = 0; if(F==false){ F = true;
k = POS - NEG NEG; ; if (k > 0){
m = m +k; NEG = NEG + k; } else {
/* re repe peri risc sco o va valo lore re da ma maga gazz zzin ino o e lo ca cari rico co su PO POS S */ F = true; za = 3; rit rit = mv mv.Preleva_Deposito( .Preleva_Deposito(za za); ); rit = rit - 10; m = 10; POS = POS + rit; rit; } } F = false ; val = m; return val; } }
49
Guido Massa Finoli Programmazione Java con Threads Oggetti B Classe_B B { public class Classe_ /* St Stat ato o dell della a Cl Clas asse se */ boolean F; BOS; ; /* val valori ori pos positi itivi vi */ int BOS Magazzino mb mb; ; za; ; int za Classe_B(Magazzino pp){ public Classe_B(Magazzino mb = pp; BOS = 10; 10; F = false ; }
flag) { public int CaricaB(int m, int flag) rit, val; val; int rit, val = 0; rit = 0; (flag g == 0){ 0){ if(fla if(F==false){ F = true; 100){ { if (BOS > 100) m = 100 + m; BOS = BOS - 100; 100; } else { /* re repe peri risc sco o va valo lore re da ma maga gazz zzin ino o e lo ca cari rico co su PO POS S */ int za = 10; rit rit = mb mb.Preleva_Deposito(za); .Preleva_Deposito(za); BOS = 100 100 * rit; rit; BOS = BOS - 100; 100; m = m + 100; }} F = false; val = m; } (flag g == 1){ 1){ else el se if(fla if(F==false){ F = true; rit = mb mb.Carica_Deposito(m);} .Carica_Deposito(m);} F = false ; val val = rit; rit; } return val; } }
Magazzino degli Oggetti A e B Magazzino no { public class Magazzi /* St Stat ato o dell della a Cl Clas asse se */ boolean F; DEP; ; /* val valori ori pos positi itivi vi */ int DEP int k; public Magazzino(){
50
Guido Massa Finoli Programmazione Java con Threads DEP = 3000 3000; ; }
public synchroniz synchronized ed int Preleva_Deposito(int z){ for(int g = 2 ; g < z ; g + + ) {
k = g*10 g*10; ; try { wait(Video.Videata_1. sMAP ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Car .println("Caricam icamento ento da Depo Deposito sito - Inte Interrot rrotto" to"); } } DEP = DEP - k; return k; } public synchroniz synchronized ed int Carica_Deposito(int val){ int zx; z x = (int) val/2; val/2;
DEP = DEP + zx; zx; try { wait(Video.Videata_1.sMAC ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Ca .println("Caric ricame amento nto da Dep Deposi osito to Interrotto"); Interrotto" ); } return zx; } }
Magazzino di scambio degli oggetti prodotti da THCS e ricevuti da THSK MagScambio io { public class MagScamb /* St Stat ato o dell della a Cl Clas asse se */ boolean F; QUA; ; /* val valori ori pos positi itivi vi */ int QUA za; ; int za public MagScambio(){
QUA = 0; F = false ; } public synchroniz synchronized ed int put(int va){
rit; int k, rit; rit rit = va; va; if (F==false){ F = true; QUA = QUA + 1; try { Thread.sleep(Video.Videata_1.sMAS ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } rit = 0; } F = false ;
51
Guido Massa Finoli Programmazione Java con Threads return rit;
} public synchroniz synchronized ed int get(){
car; int k, car; car = 0; if (F==false){ F = true; if (QUA > 0 ) {
car = 1; QUA = QUA - car; car; try { Thread.sleep(Video.Videata_1.sMAS ); ); } catch(InterruptedException (InterruptedException e){ System.out.println("Interrotto" .println("Interrotto"); ); } } } F = false ; return car;
} }
52
Guido Massa Finoli Programmazione Java con Threads L’ordine delle azioni di un thread è determinato dall’ordine delle istruzioni del programma così come sono scritte dal programmatore ( ordine del programma) _ invece invece i valori di una qualsiasi modell llo o della della memo memoria ria, la cui effe varia variabi bile le letta letta dal dal thre thread ad sono sono dete determ rmin inat atii dal dal mode ffettiv ttivaa implementazione definisce l’insieme di valori ammissibili che possono essere restituiti al momento della lettura (richiesta con una qualche istruzione presente all’interno del thread) Solitamente, ci si aspetta che questo insieme di valori ammissibili sia ad ogni istante costituito da un solo valore, coincidente con il valore più recente scritto nella variabile da leggere da un qualche thre thread ad che che ha oper operat atoo su di essa essa;; in real realtà tà il mode modell lloo dell dellaa memo memori riaa può può anch anchee oper operar aree differentemente, rendendo invisibile il valore più recente
Synchronized di blocco o metodo
Corrispondenza ordine del programma e valori della memoria
Volatile di variabile = Ultimo valore aggiornato
L’utilizzo di variabili volatili sostituisce raramente operazioni sincronizzate perché non fornisce atomicità per azioni diverse dalla semplice lettura; l’uso quindi è limitato. Il modello della memoria può
influenzare anche l’ordine di esecuzione delle istruzioni; in genere esso esso tenta tenta di garan garantire tire l’appr l’approcc occio io hap happen pens-b s-befo efore, re, ossia ossia un’is un’istru truzio zione ne che nell’o nell’ordi rdine ne del del programma è scritta prima di un’altra, si verificherà prima di questa, come il programmatore desi deside dere rere rebb bbe; e; potre potrebb bbee tutt tuttav avia ia anch anchee aver aversi si un ordi ordine ne effe effetti ttivo vo di esec esecuz uzio ione ne diffe differe rente nte dall’ordine del programma, a patto che al thread di esecuzione dello stesso l’effetto appaia come se venisse rispettato l’ordine del programma; questo eventuale cambiamento dell’ordine effettivo permette compilatori con ottimizzazioni sofisticate, e il più delle volte può essere ignorato dal programmatore
53
Guido Massa Finoli Programmazione Java con Threads
54