Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Thread
vs
Processi:
Thread
sono
pi
leggeri
dei
processi
Sfru@ano
una
comunicazione
a
memoria
condivisa
Metodo
Thread
Vantaggi:
pi
intuiDvo,
perme@e
di
istanziare
un
solo
ogge@o.
Svantaggi:
poco
essibile.
Metodo
Runnable
Vantaggi:
perme@e
di
avviare
classi
che
estendono
altre
classi
come
Thread.
Vedremo
pi
avanD
che
esistono
meccanismi
pi
avanzaD
per
la
schedulazione
di
Thread
che
preferiscono
oggeJ
di
Dpo
Runnable.
Svantaggi:
un
metodo
pi
complicato.
Non-Determinismo
class MyThread implements Runnable {! private String message;! public MyThread(String m) {! message = m;! }! public void run() {! for (int r = 0; r < 90000; r++)! System.out.println(message);! }! }! class ProvaThread {! public static void main(String[] args) {! Thread t1, t2;! MyThread r1, r2;! r1 = new MyThread("primo thread");! r2 = new MyThread("secondo thread");! t1 = new Thread(r1);! t2 = new Thread(r2);! t1.start();! t2.start();! }! }! Quale
sar
loutput?
DaD
condivisi
Pu
essere
necessario
imporre
che
certe
sequenze
di
operazioni
che
accedono
a
daD
condivisi
vengano
eseguite
dai
task
in
mutua
esclusione
class
ContoCorrente
{
private
oat
saldo;
public
ContoCorrente
(oat
saldoIniz)
{
saldo
=
saldoIniz;
}
public
void
deposito
(oat
soldi)
{
saldo
+=
soldi;
}
public
void
prelievo
(oat
soldi)
{
saldo
-=
soldi;
}
}
che succede se due Thread concorrenD cercano l'uno di depositare e l'altro di prelevare?
Operazioni
atomiche
Saldo
iniziale
=
100
Thread
1
deposito(50)
read(saldo)
->
100
sum(50+100)
->
150
write(saldo)
->
150
read(saldo)
->
150
sub(150-50)
->
100
write(saldo)
->
100
Saldo
nale
=
100
Thread
2
prelievo(50)
class ContoCorrente { private oat saldo; public ContoCorrente (oat saldoIniz) { saldo = saldoIniz; } public synchronized void deposito (oat soldi) { saldo += soldi; } public synchronized void prelievo (oat soldi) { saldo -= soldi; } }
10
Metodi synchronized
11
Quando un metodo synchronized viene invocato da un altro metodo synchronized appartenente al medesimo ogge@o, il thread chiamante non deve competere per il monitor, in quanto questulDmo gi stato acquisito durante linvocazione del primo metodo (reentrant lock) Laccesso mutuamente esclusivo vale solo per i metodi dichiaraD synchronized: laccesso a@raverso gli altri metodi non mutuamente esclusivo, cio pu avvenire anche mentre un thread ha acquisito il monitor
12
class ContoCorrente { private oat saldo; synchronized public void prelievo (oat soldi) { while (saldo-soldi<0) wait(); saldo -= soldi; } } rilascia il lock sull'ogge@o e sospende il task
13
14
PrimiDve
di
sincronizzazione
Le
primiDve
di
sincronizzazione
wait,
no?fy
e
no?fyAll
sono
associate
a
ogni
ogge@o,
in
quanto
denite
nella
classe
Object.
Consentono
a
un
thread
di
sospendersi
allinterno
di
un
monitor
(wait),
e
di
risvegliare
uno
(no?fy)
o
tuJ
(no?fyAll)
i
thread
sospesi.
Tali
primiDve
operano
sul
monitor
associato
allogge@o,
pertanto
possono
essere
invocate
allinterno
di
un
thread
solo
dopo
che
esso
ha
acquisito
il
monitor:
Cio
solo
se
il
thread
sta
eseguento
allinterno
di
un
blocco
o
metodo
synchronized
Se
si
invoca
una
di
queste
primiDve
su
un
ogge@o
per
cui
non
si
acquisito
il
monitor
si
oJene
una
IllegalMonitorStateExcep?on
Il
blocco
synchronized!
Talvolta
risulta
necessario
controllare
laccesso
concorrente
a
porzioni
di
codice
con
una
granularit
pi
ne
del
metodo
Pi
grande
la
porzione
di
codice
sincronizzata,
minore
il
parallelismo
In
quesD
casi
possibile
impiegare
il
blocco
synchronized
synchronized (obj) { ... codice critico ... }" La
semanDca
del
blocco
synchronized
simile
a
quella
dei
metodi
synchronized,
con
la
dierenza
che
il
monitor
viene
acquisito
sullogge@o
obj
anzich
su
this.
void m() { synchronized(this) { ... codice critico ...! }! }!
al
termine
noDfy();
al
termine
noDfy();
17
Priorit
e
scheduling
Assegnabile
priorit
(1-10)
ai
task
(setPriority);
default
5
Alcune
pia@aforme
supportano
il
"Dme
slicing"
In
assenza,
un
thread
viene
eseguito
no
al
completamento,
a
meno
che
non
divenD
"blocked",
"waiDng"
o
"dead"
Compito
dello
scheduler
mandare
in
esecuzione
il
thread
con
priorit
pi
alta
18
wait waiDng
dead
20
Esecutori
Sono
una
famiglia
di
classi
che
implementano
linterfaccia
ExecutorService
Si
uDlizzano
come
metodo
alternaDvo
per
eseguire
delle
classi
che
implementano
Runnable
(denite
Task)
void
submit(Runnable
task)
Schedula
lesecuzione
del
task
void
shutdown()
Impedisce
allesecutore
di
acceNare
nuovi
task
boolean
awaitTermina?on(long
?meout,
TimeUnit
unit)
A@ende
la
ne
dellesecuzione
di
tuJ
i
task
oppure
il
tempo
specicato
come
Dmeout
...
Executors."
newSingleThreadExecutor()
newFixedThreadPool(int
n)
newCachedThreadPool()
Esegue
al
massimo
un
Task
contemporaneamente
Esegue
al
massimo
n
Task
contemporaneamente
Esegue
un
numero
illimitato
di
Task
contemporaneamente
Perme@ono di disaccoppiare il conce@o di Thread dal conce@o di Task. Uno stesso thread pu eseguire pi task in sequenza senza dover essere distru@o e ricreato aumenta le prestazioni Perme@ono di avere controllo sul numero di Thread in esecuzione dando la possibilit di accodare i Task in eccesso Esistono Esecutori pi avanzaD che perme@ono di schedulare lesecuzione di task ripetuD o dopo un intervallo di tempo specicato. Esempio: Interfaccia: ScheduledExecutorService! Implementazione: Executors.newScheduledThreadPool(int n)
Deadlock
Il
deadlock
una
situazione
di
stallo
in
cui
due
(o
pi)
processi
(o
azioni)
si
bloccano
a
vicenda
aspe@ando
che
uno
esegua
una
certa
azione
(es.
rilasciare
il
controllo
su
una
risorsa
come
un
le,
una
porta
input/output
ecc.)
che
serve
allaltro
e
viceversa.
Per
evitare
il
deadlock
necessario
non
avere
dipendenze
circolari
per
acquisire
le
risorse
(lock)
Il
grafo
delle
risorse
richieste
non
deve
avere
cicli!
Esempio:
Il
thread1
ha
il
lock
su
A
e
vuole
acquisire
anche
il
lock
su
B
Il
thread2
ha
il
lock
su
B
e
vuole
acquisire
il
lock
su
A
Deadlock:
entrambi
i
thread
restano
bloccaD
nella
speranza
di
acquisire
il
lock
Esempio Deadlock
class Oggetto A {! OggettoB b;! ...! synchronized void esegui() {! ...! b.test();! ...! }! }!
class Oggetto B {! OggettoA a;! ...! synchronized void test() {! ...! a.esegui();! ...! }! }!
OggeJ
Thread-Safe
Quando
due
thread
diversi
accedono
a
un
ogge@o
contemporaneamente,
possono
vericarsi
dei
problemi
come
la
comparsa
di
ConcurrentModica?onExcep?on
o
altri
comportamenD
anomali
che
non
si
vericano
sempre.
Questo
Dpo
di
problemi
dovuto
al
non-determinismo
dellesecuzione
parallela
di
pi
thread.
Il
nome
(gergale)
con
cui
si
chiamano
queste
anomalie
heisenbug
(da
Heisemberg),
poich
tendono
a
non
essere
facilmente
riproducibili
in
un
ambiente
controllato
(es.
debugging).
Un
ogge@o
si
denisce
thread-safe
quando
perme@e
(da
contra@o)
laccesso
contemporaneo
a
pi
thread
senza
anomalie
(cio
logge@o
ore
soltanto
operazioni
atomiche).
CollecDons.synchronizedList(new ArrayList()) CollecDons.synchronizedList(new LinkedList()) CollecDons.synchronizedSet(new HashSet()) CollecDons.synchronizedSet(new TreeSet()) CollecDons.synchronizedMap(new HashMap()) CollecDons.synchronizedMap(new TreeMap())
Esercizio
1
Si
scriva
un
programma
che
esegua
2
thread.
Il
primo
thread
fa
le
seguenD
operazioni
10
volte:
Incrementa
una
variabile
della
classe
che
lo
ha
lanciato
Stampa
il
valore
di
tale
variabile
Esercizio
2
Un
call
center
possiede
5
centralinisD.
Nel
momento
di
maggior
auenza
ci
sono
100
clienD
che
chiamano.
Una
conversazione
dura
mediamente
tra
4
e
6
secondi
(per
semplicit
...).
Se
tuJ
i
centralinisD
sono
occupaD
il
cliente
resta
in
a@esa.
Si
simuli
questa
situazione
con
un
programma
Java
in
cui
si
ha
un
Thread
per
ogni
cliente.
Al
termine
di
ogni
chiamata
il
cliente
stampa
su
video
il
tempo
totale
della
sua
chiamata
(a@esa
pi
conversazione).
Esercizio
2
(estensioni)
Estensione
1:
si
modichi
la
soluzione
dellesercizio
2
uDlizzando
lEsecutore
Executors.newCachedThreadPool
Estensione
2:
si
modichi
la
soluzione
dellesercizio
2
facendo
in
modo
che
i
clienD
messi
in
a@esa
per
primi
siano
i
primi
ad
essere
serviD.