Programmazione concorrente in Java (vecchio modello)

73
Concurrent Programming in Java Guida alle API e descrizione del modello di concorrenza in Java Autore: Davide Carboni <[email protected]> © Copyright 2000-2001 CRS4 (www.crs4.it)

description

Un tutorial base sui thread in Java

Transcript of Programmazione concorrente in Java (vecchio modello)

Page 1: Programmazione concorrente in Java (vecchio modello)

Concurrent Programming in Java

Guida alle API e descrizione del modello di concorrenza in Java

Autore: Davide Carboni <[email protected]>

© Copyright 2000-2001 CRS4 (www.crs4.it)

Page 2: Programmazione concorrente in Java (vecchio modello)

Concorrenza

Fare più di una cosa per volta

Esempio:

un web browser fa il download di una pagina

HTML, suona un’ audioclip, attende un input da

tastiera, visualizza nella barra di stato il bitrate ect..

Page 3: Programmazione concorrente in Java (vecchio modello)

Ma è vera concorrenza ?

� In alcuni sistemi differenti task possono essere assegnati a differenti CPU

� In altri una singola CPU divide il proprio tempo fra i vari task , la sensazione è quella di esecuzione simultanea.

Page 4: Programmazione concorrente in Java (vecchio modello)

Quali Applicazioni

� Number Crunching

� I/O processing

� Simulation

� GUI

� Embedded Systems

Page 5: Programmazione concorrente in Java (vecchio modello)

Clusters

Ogni macchina esegue una unità di esecuzione concorrente

Facile da amministrare ma svantaggioso per la difficoltà di

condividere risorse e passare messaggi

Page 6: Programmazione concorrente in Java (vecchio modello)

Multi Processing

� Il processo è un’astrazione che fornisce il SO

� Ogni processo tipicamente rappresenta un programma durante l’esecuzione

� Ogni processo gestisce il suo spazio dati che non condivide

� La comunicazione tra processi avviene tramite IPC, pipes ...

Page 7: Programmazione concorrente in Java (vecchio modello)

Multi Threading

� Un processo può avere uno o più thread, ognuno dei quali è ancora una unità di esecuzione indipendente.

� I thread appartenenti allo stesso processo condividono la memoria, i file ed altre risorse

� le loro azioni sono sincronizzate grazie ai lock e al meccanismo wait,notify

Page 8: Programmazione concorrente in Java (vecchio modello)

Multi Threading

� I thread possono essere schedulati dalkernel nei sistemi in cui il kernel li supporta (linux, windows, …)

Page 9: Programmazione concorrente in Java (vecchio modello)

Multi Threading

� Un processo può gestire il suomultithreading internamente anche se ilkernel non supporta i thread.

� Questo viene fatto da processi come la JVM, SQL server etc…

� La JVM fornisce un costrutto concorrente anche se il SO è “monotaskoso”

Page 10: Programmazione concorrente in Java (vecchio modello)

I Thread in Java

� Java fornisce diversi strumenti per la programmazione concorrente:

java.lang.Thread class

java.lang.Object class

java.lang.Runnable interface

synchronized keyword

Page 11: Programmazione concorrente in Java (vecchio modello)

I Thread in Java (FAQ)

� Come parte un Thread ?

� Quando un Thread è in esecuzione quale codice esegue ?

� In quali/quanti stati può essere un Thread ?

� Come e perché i Thread cambiano il loro stato ?

Page 12: Programmazione concorrente in Java (vecchio modello)

Come parte un Thread ?

� Un Thread è un’istanza della classe java.lang.Thread o di una sua sottoclasse ma inizia la sua esecuzione SOLO quando viene invocato il suo metodo start()

esempio:

Thread t=new MyThread(Qualcosa);

t.start();

Page 13: Programmazione concorrente in Java (vecchio modello)

Cosa fa il metodo start()

� Il metodo start() registra l’istanza del Thread in un “pezzo di software” chiamato Thread Scheduler

� Il Thread Scheduler può essere interno alla JVM oppure esterno (servizio del SO)

� Il Thread Scheduler decide quale Thread è in esecuzione su una data CPU e in dato momento.

Page 14: Programmazione concorrente in Java (vecchio modello)

Priorità

Ogni thread ha la sua priorità che va da

1 (bassa) a 10 (alta)

t.getPriority() restituisce l’attuale priorità del Thread t

t.setPriority(8) impone a 8 la priorità del Thread t

Page 15: Programmazione concorrente in Java (vecchio modello)

Ancora l’esempio

� esempio:Thread t=new MyThread(Qualcosa);

t.start();

� a questo punto ci sono almeno 2 Thread, uno è il main che ha eseguito le due linee sopra e l’altro è t

Page 16: Programmazione concorrente in Java (vecchio modello)

Quando un Thread è in

esecuzione quale codice esegue ?

� La risposta è:

Il codice contenuto nel suo metodo run ( )

� Il metodo run( ) della classe java.lang.Thread è vuoto quindi i nostri Thread faranno l’overriding

Page 17: Programmazione concorrente in Java (vecchio modello)

Esempio:overriding di run( )

public class CounterThread extends Thread

{

public void run()

{

for(int i=1;i<=10;i++)

System.out.println(“conto “+i);

}

}

Page 18: Programmazione concorrente in Java (vecchio modello)

Esempio:lancio il Thread

CounterThread c=new CounterThread;

c.start( ); // non uso c.run( )

Se usassi c.run( ) otterrei l’esecuzione immediata di run( ) senza inserire c

nello scheduler

Page 19: Programmazione concorrente in Java (vecchio modello)

Perché questo approccio non è

soddisfacente ?

� A causa dell’ereditarietà singola la nostra classe thread non può estendere altre classi.

� Questo è un grosso problema nel design di architetture complesse.

Page 20: Programmazione concorrente in Java (vecchio modello)

java.lang.Runnable

� La classe java.lang.Thread ha un

secondo costruttore :

public Thread(Runnable eseguibile)

� L’interfaccia Runnable ha il solo metodo

public void run();

Page 21: Programmazione concorrente in Java (vecchio modello)

java.lang.Runnable

� Grazie a Runnable posso costruire

architetture più complessepublic class Derived extends Base

implements Runnable{

public void run( ) {

faiqualcosainmultithreading;}

public int f(int x){ ...}

public Object g(byte [] x){ ...}

}

Page 22: Programmazione concorrente in Java (vecchio modello)

java.lang.Runnable

� Derived d=new Derived( … );

� Thread t=new Thread(d);

� t.start( );

� d.f(x); //non c’entra con i thread

Page 23: Programmazione concorrente in Java (vecchio modello)

La morte dei Thread

� I thread “muoiono” quando il metodo run ( ) ritorna

� In realtà sono dei non-morti perché pur non essendo più eseguibili sono ancora degli oggetti Java validi e tutti i loro metodi possono essere ancora invocati

Page 24: Programmazione concorrente in Java (vecchio modello)

Gerarchia di classi

Class Object

Class Base

Class Derived

Class Thread

Runnable

Page 25: Programmazione concorrente in Java (vecchio modello)

La morte dei Thread

� Valgono perciò 2 regole:

1. Un thread il cui metodo run( ) ritorna NON può essere più “restartato”

2. Se l’istanza del thread non viene Gcollected i suoi metodi possono essere invocati

Page 26: Programmazione concorrente in Java (vecchio modello)

La morte dei Thread

� Non è una buona strategia quella di creare, usare e buttare via dei thread perché la costruzione e lo scheduling di un thread è un’operazione onerosa.

� Per ovviare a questo esistono tecniche di Thread Pooling, ossia mi tengo un’insieme di Thread pronti all’uso

Page 27: Programmazione concorrente in Java (vecchio modello)

Esempio: un web server in java

public class Server

{

int port=9000;

Socket socket=null;

ServerSocket sock_serv=null;

public void boot(int port) { …}

[snip]

}

Page 28: Programmazione concorrente in Java (vecchio modello)

Esempio: un web server in java

public void boot(int port) throws Exception

{

this.port=port;

sock_serv=new ServerSocket(port);

MyThread aThread=null;

while(true)

{

socket=sock_serv.accept();

aThread=new MyThread(socket);

aThread.start();

}

}

Page 29: Programmazione concorrente in Java (vecchio modello)

Esempio: un web server in java

public class MyThread extends Thread

{

Socket socket=null;

MyThread(Socket s)

{

socket=s;

}

public void run()

{

…[implementation]…

}

}

Page 30: Programmazione concorrente in Java (vecchio modello)

Esempio: un web server in java

public void run(){

try{

PrintWriter writer=new

PrintWriter(socket.getOutputStream(),true);

BufferedReader reader=

new BufferedReader(

new

InputStreamReader(socket.getInputStream()));

[snip]

Page 31: Programmazione concorrente in Java (vecchio modello)

Esempio: un web server in java

public void run(){

[snip]

String input=reader.readLine();

if(input.startsWith("GET"))

{

writer.println("<html><body><p><h1>");

writer.println("CIAO A TUTTI");

writer.println("</h1></p></body></html>");

}

socket.close();

return;

}//end of try block

catch(Exception e){ … }

}

Page 32: Programmazione concorrente in Java (vecchio modello)

In quali/quanti stati può essere un

Thread ?

� Running

� Ready-to-run

� Suspended, Blocked, Sleeping (not ready)

� Monitor states (sync,wait/notify sequence)

� Dead

Page 33: Programmazione concorrente in Java (vecchio modello)

In quali/quanti stati può essere un

Thread ?

running

Ready-to-run

suspended sleeping blockedMonitor

states

Page 34: Programmazione concorrente in Java (vecchio modello)

Transizioni di stato

running

Ready-to-run

� t.yield( ) causa al

Thread t il passaggio da running a ready-to-run

� in questo modo un Thread diverso da t ha la possibilità immediata di passare allo stato running

Page 35: Programmazione concorrente in Java (vecchio modello)

Transizioni di stato

suspend( ) e resume( ) sono

deprecati in java1.2

running

Ready-to-run

suspended

suspend()

resume()

Page 36: Programmazione concorrente in Java (vecchio modello)

Transizioni di stato

� Thread.sleep(long)

causa al thread che

lo invoca il

passaggio allo stato

sleeping

� E’ un sistema di

timing ma non è

precisissimo

running

Ready-to-run

sleeping

sleep(long)

Time expires

or interrupted

Page 37: Programmazione concorrente in Java (vecchio modello)

Transizioni di stato

� Alcuni metodi

causano il blocco

del thread

invocante, questo

capita quando di

mezzo c’è un device

che non risponde

subito come un’

interfaccia di rete

running

Ready-to-run

blocked

Blocking

method

Interruption

or change in

blocking

device

Page 38: Programmazione concorrente in Java (vecchio modello)

Blocco di un Thread

aSocket=new Socket(“www.unica.it”,80);

InputStream i=aSocket.getInputStream( );

int b = i.read();

// qui ci possiamo bloccare

Se la “rete” non è pronta a fornirci il int richiesto il thread in esecuzione si blocca !!

Page 39: Programmazione concorrente in Java (vecchio modello)

Perché bisogna sincronizzare

Cattivo Esempio

class Pari{

private int n=0;

public int next(){

n++; n++;

return n;

}

}

Page 40: Programmazione concorrente in Java (vecchio modello)

Perché bisogna sincronizzare

� Thread A

– read 0

– write 1

– read 2

– write 3

– return 3

� Thread B

– read 1

– write 2

– read 2

– write 3

– return 3

Page 41: Programmazione concorrente in Java (vecchio modello)

Codice sincronizzato

� Ogni istanza di java.lang.Object o di

una sua sottoclasse possiede un LOCK

� Un valore primitivo (int,long,…) non possiede

nessun LOCK

� Un array di tipi primitivi o Object possiede un

LOCK

Page 42: Programmazione concorrente in Java (vecchio modello)

synchronized

synchronized(a)

{

istruz_1;

istruz_2;

istruz_k;

}

� Solo il thread che

acquisisce il LOCK

di a può eseguire il

blocco.

� Gli altri thread in

competizione per il

LOCK si bloccano in

uno stato detto di

seeking

Page 43: Programmazione concorrente in Java (vecchio modello)

synchronized

L’approccio precedente consente di sincronizzare un blocco di codice di un oggetto O con un LOCK di un altro oggetto a

Potente possibilità ma pericoloso !

Page 44: Programmazione concorrente in Java (vecchio modello)

Lo stato seeking lock

running

scheduled

Enter

synchronized

code

Lock obtainedready to run

seeking

Page 45: Programmazione concorrente in Java (vecchio modello)

synchronized

Object f( )

{

synchronized(this){ ...codice... }

}

EQUIVALE

synchronized Object f( )

{

...codice...

}

Page 46: Programmazione concorrente in Java (vecchio modello)

synchronized

Class S{

public synchronized void f( ) { … }

public synchronized void g( ) { … }

public void h( ) { … }

}

invocare f() e g() su istanze di S ci costringe a competere per il lock, però possiamo invocare liberamente h( )

Page 47: Programmazione concorrente in Java (vecchio modello)

synchronized

O = new S( );

O.f( ); //aspetto che il LOCK sia mio

… ; //al ritorno di f( ) ho rilasciato

il LOCK

O.h( ); // la eseguo e basta

O.g( ); // aspetto che il LOCK sia mio

… ; //ho già rilasciato il LOCK

Page 48: Programmazione concorrente in Java (vecchio modello)

Atomicità

Un oggetto si dice atomico se:

� tutti i metodi sono sincronizzati

� non ci sono attributi pubblici o altre violazioni dell’incapsulamento

� tutti i metodi sono finiti (senza loop infiniti)

� lo stato dell’oggetto è costruito in modo consistente

� lo stato è consistente sia alla fine che all’inizio di ogni metodo anche in presenza di eccezioni

Page 49: Programmazione concorrente in Java (vecchio modello)

Thread Safe

Le classi di oggetti che implementano l’atomicità sono thread safe, ossia il

loro stato non può essere corrotto dall’esecuzione di più thread

simultaneamente

Page 50: Programmazione concorrente in Java (vecchio modello)

Deadlock

� Anche se un oggetto atomico è thread safe i

thread che lo usano possono finire in uno

stato detto Deadlock (abbraccio mortale)

lock

lock

O1 O2

Page 51: Programmazione concorrente in Java (vecchio modello)

Monitor

� Un monitor è un oggetto che può bloccare e risvegliare thread

� In Java ogni oggetto dotato di codice synchronized è un monitor

Page 52: Programmazione concorrente in Java (vecchio modello)

Monitor: ma a che servono?

� Attraverso le primitive wait e notify possiamo coordinare le attivita’ di 2 o piu’ thread

Page 53: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

public class SpaceProvider implements

net.jini.discovery.DiscoveryListener

{

[snip]

private JavaSpace space;

[snip]

public synchronized JavaSpace getSpace(String category) throws

Exception { … }

[snip]

public synchronized void discovered(DiscoveryEvent e) { …. }

}

Page 54: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

public synchronized JavaSpace getSpace(String

category) ... {

if(space==null){

LookupDiscovery ld=new LookupDiscovery(…);

ld.addDiscoveryListener(this);

wait(60000);

}

if(space == null) {

throw new Exception ("No space found”);

}

return space;

}

Questo thread si blocca

in monitor-state

Page 55: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

public synchronized void discovered(DiscoveryEvent e) {

ServiceRegistrar reggie=e.getRegistrars()[0];

[snip]

try{

space=(JavaSpace)reggie.lookup(…);

if(space!=null)

{

notify();

}

}

catch(Exception ex){

ex.printStackTrace();

}

}

A questo punto l’altro thread passa

in ready-to run

Page 56: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

public synchronized JavaSpace getSpace(String

category) ... {

if(space==null){

LookupDiscovery ld=new LookupDiscovery(…);

ld.addDiscoveryListener(this);

wait(60000);

}

if(space == null) {

throw new Exception ("No space found”);

}

return space;

}

Page 57: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

Ricapitolando

Un monitor e’ un qualsiasi oggetto

che possiede del codice synchronized

Page 58: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

Ricapitolando

� wait causa al thread che la

esegue la cessione del lock ed il

blocco in monitor-state.

�Se il thread che esegue wait non

detiene il lock viene lanciata un’eccezione

Page 59: Programmazione concorrente in Java (vecchio modello)

Esempio di wait, notify

Ricapitolando

�notify causa al thread che la

esegue la cessione del lock

�notify risveglia un thread che ha

eseguito wait sul monitor

� Il thread risvegliato passa dallo stato monitor-state allo stato ready-to-run

Page 60: Programmazione concorrente in Java (vecchio modello)

Altri modi per coordinare I thread

� wait e notify coordinano le attivita’

dei thread usando un lock come elemento di coordinazione

� join coordina un thread

sull’esecuzione di un’altro thread

Page 61: Programmazione concorrente in Java (vecchio modello)

Altri modi per coordinare i thread

public static void main(String[] args) �…{

System.out.println("main thread:messaggio 1");

Runnable r1= new Runnable(){

public void run(){

for (int i= 0; i < 10; i++) {

System.out.println("r1 thread:messaggio "+ i);

try{Thread.sleep(1000);}

catch (Exception e)

{e.printStackTrace();}

}

}

};

…[snip]

Page 62: Programmazione concorrente in Java (vecchio modello)

Altri modi per coordinare I thread

…[snip]

Thread t1= new Thread(r1);

t1.start();

System.out.println("main thread:messaggio 2");

t1.join();

System.out.println("main thread:messaggio 3");

}

Page 63: Programmazione concorrente in Java (vecchio modello)

Altri modi per coordinare I threadOutput:

main thread:messaggio 1

r1 thread:messaggio 0

main thread:messaggio 2

r1 thread:messaggio 1

r1 thread:messaggio 2

r1 thread:messaggio 3

r1 thread:messaggio 4

r1 thread:messaggio 5

r1 thread:messaggio 6

r1 thread:messaggio 7

r1 thread:messaggio 8

r1 thread:messaggio 9

main thread:messaggio 3

Page 64: Programmazione concorrente in Java (vecchio modello)

Altri modi per coordinare I thread

t

Main thread messaggio 1

Main thread messaggio 2

r1 thread messaggio 0

r1 thread messaggio 1

r1 thread messaggio 9

….

Main thread messaggio 3

main()

t1.start()

t1.join()

Page 65: Programmazione concorrente in Java (vecchio modello)

Limiti del modello

� Operazioni deprecate stop(), suspend(), resume()

� Mancanza del back off da seeking-lock

� Denial-of-service in caso di synchronized(obj)

� Block structured Locking

Page 66: Programmazione concorrente in Java (vecchio modello)

Operazioni deprecate

stop()

Questa operazione e’ thread-unsafe perche' costringe il thread a terminare rilasciando i lock degli oggetti.

Gli oggetti rilasciati possono finire in uno stato inconsistente.

Page 67: Programmazione concorrente in Java (vecchio modello)

Operazioni deprecate

suspend(),resume()

Sospendere un thread e’ pericoloso perche’ il thread sospeso mantiene i lock degli oggetti senza procedere.

Page 68: Programmazione concorrente in Java (vecchio modello)

Operazioni deprecate

suspend(),resume()

Esempio: 2 Thread T1 e T2

T1 possiede il lock O

T2 sospende T1

T2 cerca di eseguire un blocco sincronizzato su O

T2 e’ bloccato in seeking-lock su O

T1 non rilascera’ mai O perche’ e’ sospeso

T1 e T2 sono entrambi bloccati, in assenza di un terzo thread il sistema resta bloccato.

Page 69: Programmazione concorrente in Java (vecchio modello)

Mancanza del back off da

seeking-lock

� Un thread T che cerca di eseguire codice sincronizzato su un lock O viene messo in competizione per l’acquisizione di O e NON puo’ tirarsi indietro

Page 70: Programmazione concorrente in Java (vecchio modello)

Denial-of-service

� Qualunque metodo puo sincronizzare parte del suo codice su un qualunque oggetto O di cui abbia visibilita’ tramite synchronized(O)

� Un meccanismo di accesso controllato ai lock potrebbe ridurre la possibilita; di denial-of-service

Page 71: Programmazione concorrente in Java (vecchio modello)

Block structured Locking

� Lo scope di acquisizione rilascio e’ strettamente a livello di blocco o di metodo, non e’ possibile acquisire il lock in un metodo M1 e rilasciarlo in un metodo M2.

Page 72: Programmazione concorrente in Java (vecchio modello)

Bibliografia

� Thinking in Java, Bruce Eckel, Prentice Hall

� Concurrent Programming in Java, Doug Lea,

Addison Wesley

� The complete Java 2 certification guide,

S.Roberts et al., Sybex

� The Java Language Specifications, J. Gosling

et al., Addison Wesley

Page 73: Programmazione concorrente in Java (vecchio modello)

Q & A