2. 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..
3. 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.
5. Clusters
Ogni macchina esegue una unità di esecuzione concorrente
Facile da amministrare ma svantaggioso per la difficoltà di
condividere risorse e passare messaggi
6. 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 ...
7. 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
8. Multi Threading
I thread possono essere schedulati dal
kernel nei sistemi in cui il kernel li
supporta (linux, windows, …)
9. Multi Threading
Un processo può gestire il suo
multithreading internamente anche se il
kernel 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”
10. 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
11. 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 ?
12. 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();
13. 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.
14. 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
15. 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
16. 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
17. Esempio:overriding di run( )
public class CounterThread extends Thread
{
public void run()
{
for(int i=1;i<=10;i++)
System.out.println(“conto “+i);
}
}
18. 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
19. 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.
20. 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();
21. java.lang.Runnable
Grazie a Runnable posso costruire
architetture più complesse
public class Derived extends Base
implements Runnable{
public void run( ) {
faiqualcosainmultithreading;}
public int f(int x){ ...}
public Object g(byte [] x){ ...}
}
23. 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
25. 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
26. 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
27. 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]
}
28. 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();
}
}
29. Esempio: un web server in java
public class MyThread extends Thread
{
Socket socket=null;
MyThread(Socket s)
{
socket=s;
}
public void run()
{
…[implementation]…
}
}
30. 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]
…
31. Esempio: un web server in java
public void run(){
…
[snip]
…
String input=reader.readLine();
if(input.startsWith(quot;GETquot;))
{
writer.println(quot;<html><body><p><h1>quot;);
writer.println(quot;CIAO A TUTTIquot;);
writer.println(quot;</h1></p></body></html>quot;);
}
socket.close();
return;
}//end of try block
catch(Exception e){ … }
}
32. In quali/quanti stati può essere un
Thread ?
Running
Ready-to-run
Suspended, Blocked, Sleeping (not
ready)
Monitor states (sync,wait/notify
sequence)
Dead
33. In quali/quanti stati può essere un
Thread ?
running
Monitor
suspended sleeping blocked
states
Ready-to-run
34. Transizioni di stato
t.yield( ) causa al
running Thread t il passaggio da
running a ready-to-run
in questo modo un
Thread diverso da t ha la
Ready-to-run possibilità immediata di
passare allo stato running
35. Transizioni di stato
suspend()
running
suspend( ) e
suspended resume( ) sono
deprecati in
java1.2
Ready-to-run resume()
36. Transizioni di stato
Thread.sleep(long)
sleep(long) causa al thread che
running
lo invoca il
passaggio allo stato
sleeping sleeping
E’ un sistema di
Ready-to-run Time expires
or interrupted timing ma non è
precisissimo
37. Transizioni di stato
Alcuni metodi
Blocking causano il blocco
running method
del thread
invocante, questo
blocked capita quando di
mezzo c’è un device
che non risponde
Ready-to-run Interruption subito come un’
or change in
blocking interfaccia di rete
device
38. 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 !!
39. Perché bisogna sincronizzare
Cattivo Esempio
class Pari{
private int n=0;
public int next(){
n++; n++;
return n;
}
}
41. 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
42. synchronized
synchronized(a) Solo il thread che
{ acquisisce il LOCK
istruz_1; di a può eseguire il
istruz_2; blocco.
… Gli altri thread in
istruz_k; competizione per il
} LOCK si bloccano in
uno stato detto di
seeking
46. 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( )
47. 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
48. 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
49. 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
50. 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
51. Monitor
Un monitor è un oggetto che può
bloccare e risvegliare thread
In Java ogni oggetto dotato di codice
synchronized è un monitor
52. Monitor: ma a che servono?
Attraverso le primitive wait e notify
possiamo coordinare le attivita’ di 2 o
piu’ thread
53. 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) { …. }
}
54. Esempio di wait, notify
public synchronized JavaSpace getSpace(String
category) ... {
if(space==null){
LookupDiscovery ld=new LookupDiscovery(…);
ld.addDiscoveryListener(this);
wait(60000);
Questo thread si blocca
} in monitor-state
if(space == null) {
throw new Exception (quot;No space found”);
}
return space;
}
55. 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();
A questo punto l’altro thread passa
} in ready-to run
}
catch(Exception ex){
ex.printStackTrace();
}
}
56. 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 (quot;No space found”);
}
return space;
}
57. Esempio di wait, notify
Ricapitolando
Un monitor e’ un qualsiasi oggetto
che possiede del codice
synchronized
58. 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
59. 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
60. 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
61. Altri modi per coordinare i thread
public static void main(String[] args) …{
System.out.println(quot;main thread:messaggio 1quot;);
Runnable r1= new Runnable(){
public void run(){
for (int i= 0; i < 10; i++) {
System.out.println(quot;r1 thread:messaggio
quot;+ i);
try{Thread.sleep(1000);}
catch (Exception e)
{e.printStackTrace();}
}
}
};
…[snip]
62. Altri modi per coordinare I thread
…[snip]
Thread t1= new Thread(r1);
t1.start();
System.out.println(quot;main thread:messaggio 2quot;);
t1.join();
System.out.println(quot;main thread:messaggio 3quot;);
}
63. Altri modi per coordinare I thread
Output:
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
64. Altri modi per coordinare I thread
main()
Main thread messaggio 1
t1.start()
r1 thread messaggio 0
Main thread messaggio 2
t1.join() r1 thread messaggio 1
….
r1 thread messaggio 9
t Main thread messaggio 3
65. 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
66. 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.
67. Operazioni deprecate
suspend(),resume()
Sospendere un thread e’ pericoloso
perche’ il thread sospeso mantiene i
lock degli oggetti senza procedere.
68. 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.
69. 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
70. 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
71. 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.
72. 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