2. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Els
fils
(threads)
tenen
un
costat
f o s c :
P o d e n d o n a r l l o c a
problemes de concurrència.
Els problemes de concurrència
donen
lloc
a
“condicions
de
carrera“
(race
condi*ons).
Les
“condicions
de
carrera”
donen
lloc
a
dades
corruptes.
Hem
arribat
a
una
situació
fa1dica:
Dos
o
més
fils
accedint
a
les
dades
d’un
únic
objecte.
Quan
un
fil
no
està
acMu
o
corrent
és
com
si
esMgués
inconscient.
Quan
(de
nou)
és
acMvat
no
sap
que
ha
estat
aturat.
3. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
1:
Molta
llet
Hora
Persona
1
Persona
2
15:00
Mirar
en
la
nevera:
No
hi
ha
llet!!
15:05
SorMr
al
carrer
15:10
Arribar
a
la
boMga
Mirar
en
la
nevera:
No
hi
ha
llet!!
15:15
Comprar
un
tetra-‐brick
de
llet
SorMr
al
carrer
15:20
Posar
el
brick
de
llet
en
la
Arribar
a
la
boMga
nevera
15:25
Comprar
un
tetra-‐brick
de
llet
15:30
Posar
el
brick
de
llet
en
la
nevera.
Ep!!
Ja
n’hi
ha!!
4. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
1:
Molta
llet
Variable compartida Tasca a realitzar
5. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
1:
Molta
llet
Iniciem els fils
d’execució amb la
tasca a realitzar.
Comparteixen el
mateix objecte
nevera!!
6. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
En
Ricard
s’adorm
després
d’haver
La
TaMana
-‐
parella
de’n
comprovat
el
saldo
del
compte
Ricard
-‐
se’n
va
a
un
corrent
(100€)
però
abans
de
caixer
per
treure
diners
confirmar
l’execució
d’un
traspàs
(90€).
Consulta
el
saldo
de
90€
a
un
altre
compte
corrent.
per
confirmar
que
tot
està
correcte
(100€).
I
treu
els
diners.
Quan
en
Ricard
es
desperta,
immediatament
finalitza
l’execució
del
traspàs
de
diners
sense
haver
comprovar
(un
altre
cop)
el
saldo
del
compte
corrent.
→
El
saldo
del
compte
corrent
passa
a
estar
en
números
vermells!!
7. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
Què
ha
passat?
Runnable
2 fils (Ricard i Tatiana) comparteixen un mateix
objecte (el compte corrent).
El
codi
té
dues
classes,
Comptecorrent,
i
TascaTatiRicard.
CompteCorrent
La
classe
TascaTatiRicard
implementa
un
Runnable i
representa
el
comportament
que
tenen
int saldo
tant
en
Ricard
com
la
TaMana
–
comprovar
el
saldo
del
compte
i
reMrar
diners
–.
Lògicament,
cada
fil
mostraSaldo()
(thread)
cau
adormit
entre
les
dues
comprovacions
retirar()
del
saldo
i,
de
fet,
quan
es
fa
la
reMrada
de
diners.
TascaTatiRicard
La
classe
TascaTatiRicard
té
una
variable
de
Mpus
compteCorrent compte
CompteCorrent
que
representa
el
seu
propi
compte
corrent
comparMt.
run()
retirarDiners()
8. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
q Creem una instància de TascaTatiRicard.
La
classe
TascaTatiRicard
és
el
Runnable
(la
feina
a
fer)
I,
donat
que
tant
la
TaM
com
en
Ricard
fan
el
mateix
(comprovar
el
saldo
I
reMrar
diners),
necessitem
realitzar
una
única
instància.
tascaTatiRicard laTasca = new tascaTatiRicard ();
q Creem 2 fils amb el mateix Runnable
(la instància TascaTatiRicard)
thread u = new Thread (laTasca);
thread dos = new Thread (laTasca);
q Anomenem i arranquem els fils (threads)
u.setName (”Ricard”);
dos.setName (”Tatiana”) ;
u.start( );
dos.start( );
9. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
q Observem com els fils executen el mètode run ()
(comprovar el saldo I retirar diners)
Un
fil
representa
en
Ricard
I
l’altre
representa
la
TaMana.
Els
dos
fils
estan
congnuament
comprovant
el
saldo
i
realitzen
una
reMrada
de
diners
sempre
I
quan
sigui
possible.
if (CompteCorrent.mostrarSaldo ( ) ≥ quantitat) {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {ex.printstackTrace ( ); }
}
10. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
class
compteCorrent
{
El compte comença
private
int
saldo
=
100;
amb un saldo de
public
int
mostrarSaldo
()
{
100€.
return
saldo;
}
public
void
reMrar
(int
quanMtat)
{
saldo
=
saldo
-‐
quanMtat;
Únicament hi haurà UNA instància
}
de tascaTatiRicard. Això vol dir
}
solament UNA instància del compte
corrent. Ambdós fils accediran a
aquest únic compte corrent.
public
class
tascaTaMRicard
implements
Runnable
{
private
compteCorrent
compte
=
new
CompteCorrent();
Instanciem el Runnable (tasca)
public
staMc
void
main
(String
[]
args)
{
tascaTaMRicard
laTasca
=
new
tascaTaMRicard
();
Thread
u
=
new
Thread
(laTasca);
Construïm dos fils; donem a cadascun d’ells
Thread
dos
=
new
Thread
(laTasca);
la mateixa feina del Runnable. Ambdós fils
u.setName(“Ricard”);
accediran a la variable de instància de
dos.setName(“TaMana”);
compte en la classe Runnable.
u.start();
dos.start();
}
11. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
public
void
run
()
{
En el mètode run (), un fil entra en un bucle i
for
(int
x
=
0;
x
<
10;
x++)
{
intenta fer una retirada amb cada iteració.
reMrarDiners
(10)
;
Després de la retirada de diners, comprova el
if
(compteCorrent.mostraSaldo()
<
0)
{
saldo un cop més per comprovar si el compte
System.out.println(“Números
vermells");
està en números vermells.
}
}
Comprovem el saldo del compte i, si no hi ha prou
}
diners, mostrem un missatge. Si hi ha prou diners,
ens anem a dormir; posteriorment, ens despertem i
private
void
reMrarDiners
(int
quanMtat)
{
acabem la retirada (tal i com va fer en Ricard).
if
(compte.mostrarSaldo()
>=
quanMtat)
{
System.out.println(Thread.currentThread()
.getName()
+
“
està
a
punt
de
reMrar
diners”);
try
{
System.out.println
(Thread
.
currentThread()
.getName()
+
“
s’adormirà”);
Thread
.sleep(500);
}
catch(InterruptedExcepMon
ex)
{ex.printStackTrace();
}
System.out.println
(Thread.
currentThread()
.getName()
+
“
s’ha
despertat”);
compte.reMrar(quanMtat);
System.out.println
(Thread.
currentThread()
.getName()
+
“
Completa
la
reMrada”);
}
else
{
System.out.println(“Ho
senMm,no
hi
ha
fons
per
”
+
Thread.currentThread().getName());
}
}
Imprimim per pantalla un grapat de declaracions per poder veure el que està succeint
}
mentre s'executa.
12. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
El
mètode
reMrarDiners()
sempre
comprova
el
saldo
abans
de
realitzar
la
reMrada
de
diners;
tot
i
això,
el
compte
sempre
es
queda
en
números
vermells.
Un possible escenari:
En
Ricard
comprova
el
saldo,
mira
que
hi
ha
prou
diners
i,
llavors,
s’adorm.
Mentrestant,
la
TaMana
va
i
comprova
el
saldo.
Ella,
també,
veu
que
hi
ha
prou
diners.
No
té
ni
idea
que
en
Ricard
es
despertarà
I
completarà
la
reMrada
de
diners.
En
Ricard
es
desperta
i
enllesteix
la
reMrada
de
diners.
La
TaM
“es
desperta”
I
completa
la
seva
reMrada.
Gran
problema!!
Entre
el
moment
en
que
ella
ha
comprovat
el
saldo
i
que
ha
fet
la
reMrada,
en
Ricard
s’ha
despertat
i
ha
reMrat
diners
del
compte..
La
comprovació
del
compte
per
part
de
la
TaMana
no
és
vàlida
perquè
en
Ricard
ja
havia
comprovat
I
estava
enmig
de
la
realització
de
la
reMrada.
L’accés
al
compte
per
part
de
la
TaMana
s’hagués
hagut
d’aturar
fins
que
en
Ricard
hagués
despertat
I
hagués
finalitzat
la
seva
transacció.
I
a
l’inrevés,
també.
13. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
14. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
Necessiten un cadenat
per gestionar l’accés al
compte
El
cadenat
treballaria
tal
I
com
segueix:
1 Hi
hauria
un
cadenat
associat
amb
les
transaccions
dels
comptes
bancaris
(comprovació
del
saldo
i
reMrada
de
diners).
Solament
hi
hauria
una
clau
que
es
manMndria
amb
el
cadenat
fins
que
algú
La transacció del
volgués
accedir
al
compte.
compte bancari està
oberta quan ningú està
utilitzant el compte.
15. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
2 Quan
en
Ricard
volgués
accedir
al
compte
corrent
del
banc
(per
comprovar
el
saldo
i
realitzar
una
reMrada
de
diners),
tancaria
el
cadenat
I
es
guardaria
la
cau
a
la
butxaca.
Quan en Ricar d vol
accedir al compte,
tanca el cadenat i es
queda amb la clau.
En
conseqüència,
ningú
més
podria
accedir
al
compte
donat
que
la
clau
ja
no
estaria
disponible.
16. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
3 En
Ricard
manKndria
la
clau
a
la
butxaca
fins
que
finalitzés
la
transacció.
Com
que
ell
Mndria
l’única
clau
pel
cadenat,
la
TaMana
no
podria
accedir
al
compte
(o
a
la
comprovació)
fins
que
en
Ricard
hagués
deixat
el
cadenat,
de
nou,
obert.
Inclús
si
en
Ricard
caigués
adormit
després
de
Quan en Ricard acaba,
obre el cadenat i
comprovar
el
saldo,
Kndria
la
garanKa
de
que
retorna la clau. Ara la
el
saldo
seria
el
mateix
quan
es
despertés
clau està disponible
per la Tati o per en
perquè
mentre
hagués
dormit,
hauria
Kngut
Ricard per si, de nou,
la
clau
guardada
en
la
seva
butxaca.
vol accedir al compte.
17. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
Necessitem
que
el
mètode
reMrarDiners
()
funcioni
com
quelcom
atòmic.
Necessitem
assegurar-‐nos
de
que,
un
cop
que
el
fil
entri
en
el
mètode
reMrarDiners
(),
pugui
finalitzar
el
mètode
abans
de
que
qualsevol
altre
fil
pugui
entrar-‐hi.
És
a
dir,
que
un
cop
que
un
fil
hagi
comprovat
el
saldo,
aquest
Mndrà
la
garanMa
de
que
pot
adormir-‐se
i
despertar
més
tard
i
finalitzar
la
reMrada
de
diners
abans
de
que
qualsevol
altre
fil
pugui
comprovar
el
saldo.
18. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
Necessitem
que
el
mètode
reMrarDiners
()
funcioni
com
quelcom
atòmic.
En programació, una acció ATÒMICA és
aquella que succeix amb efectivitat en/
d’un sol vez. Una acció atòmica no es
pot aturar a mig fer (o succeeix
completament o no succeeix en
absolut).
19. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
Si
uMlitzem
la
paraula
clau
synchronized
podrem
modificar
un
mètode
perquè
tant
sols
un
fil
pugui
accedir-‐hi.
En
el
cas
de
l’exemple,
serà
així
com
protegirem
el
compte
corrent
del
banc.
La paraula clau synchronized
No
posarem
un
cadenat
al
compte
del
banc.
vol dir que un fil necessita
una clau per accedir al codi
sincronitzat.
Posarem
el
cadenat
al
que
realitza
Per pr ote gir les nostr es
la
transacció
bancària.
Així,
un
fil
podrà
d a d e s , h a u r e m d e
completar
tota
una
transacció
–
de
principi
a
sincronitzar els mètodes que
actuïn sobre aquestes dades.
fi
–
inclús
si
el
fil
s'adormís
a
meitat
del
mètode.
20. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
2:
Two
people,
one
account
–
Codi
resultant
private
void
reMrarDiners
(int
quanMtat)
{
If
(compte.mostrarSaldo()
>=
quanMtat)
{
System.out.println(Thread.currentThread().getName()
+"
està
a
punt
de
reMrar
diners");
try
{
System.out.println
(Thread.currentThread().getName()
+"
s'adormirà");
Thread.sleep(500);
}
catch(InterruptedExcepMon
ex)
{ex.printStackTrace();
}
System.out.println
(Thread.currentThread().getName()
+
"
ja
s'ha
despertat");
compte.reMrar(quanMtat);
System.out.println
(Thread.currentThread().getName()
+
"
completa
la
reMrada");
}
else
{
System.out.println("Ho
senMm,no
hi
ha
fons
per
"
+
Thread.currentThread().getName());
}
}
}
21. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
1:
Molta
llet
-‐
Solució
La
sincronització
permet
definir
ordres
estrictes
d’accés
i
execució.
22. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Cadenats
i
sincronització
• Cada
objecte
de
Java
té
un
cadenat.
Un
cadenat
té
una
única
clau.
La
majoria
del
temps,
estan
oberts.
• Els
cadenats
dels
objectes
entren
en
joc
quan
hi
ha
mètodes
sincronitzats.
Aquests
cadenats
és
el
que
coneixerem
com
a
.
• La
finalitat
de
la
sincronització
és
protegir
les
dades
críMques.
No
protegirem
sota
cadenat
les
dades,
sincronitzarem
els
mètodes
que
accedeixin
a
aquestes
dades.
23. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
3:
El
temut
problema
de
l’actualització
perduda
Hi
ha
un
altre
problema
clàssic
de
concurrència
que
prové
del
món
de
les
bases
de
dades.
L’actualització
perduda
gira
al
voltant
d’un
procés:
Pas 1: Agafa el saldo del compte
int i = saldo;
Pas 2: Suma 1 a aquest saldo
saldo = i + 1;
El truc per mostrar això és forçar l'equip a fer 2 passos per completar el
canvi en l'equilibri. En el món real, faríem aquest moviment particular
en una sola sentència: saldo++;
Ara bé, forçant-ho a 2 passos, poden aparèixer els problemes propis
d’un procés no atòmic.
24. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
3:
El
temut
problema
de
l’actualització
perduda
Imaginem
que,
enlloc
dels
passos
trivials
“obMngues
el
saldo
i
suma-‐hi
1”,
els
dos
(o
més
passos)
d’aquest
mètode
són
molt
més
complexos
i,
per
tant,
no
es
pot
fer
amb
una
única
sentència.
En
el
problema
de
l’actualització
perduda,
tenim
2
fils,
ambdós
intentant
incrementar
el
saldo.
25. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
3:
El
temut
problema
de
l’actualització
perduda
class
TestSync
implements
Runnable
{
private
int
saldo;
Cada
fil
corre
50
cops,
incrementant
el
saldo
public
void
run
()
{
for
(int
i=0;
i<50;
i++)
{
amb
cada
iteració.
increment
();
System.out.println(“El
saldo
és
“
+
saldo);
}
}
Aquí
hi
ha
el
punt
crucial!!
Incrementem
public
void
increment
()
{
el
saldo
afegint
1
a
sigui
quin
sigui
el
int
I
=
saldo;
valor
del
saldo
en
el
moment
de
llegir-‐
saldo
=
I
+
1;
}
ho
(enlloc
d’afegir
1
a
sigui
quin
sigui
el
}
valor
actual).
public
class
TestSyncTest
{
public
staMc
void
main
(String[]
args)
{
TestSync
tasca
=
new
TestSync
();
Thread
a
=
new
Thread
(tasca);
Thread
b
=
new
Thread
(tasca);
a.start
();
b.start
();
}
}
26. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
3:
El
temut
problema
de
l’actualització
perduda
1 El
fil
A
funciona
durant
un
temps:
2 El
fil
B
funciona
durant
un
temps:
• Posem
el
valor
de
saldo
en
la
variable
i.
• Posem
el
valor
de
saldo
en
la
variable
i.
• La
variable
saldo
és
0,
per
tant,
i
ara
és
0.
• La
variable
saldo
és
2,
per
tant,
i
ara
és
2.
• Actualitzem
saldo
amb
el
resultat
de
i+1.
• Actualitzem
saldo
amb
el
resultat
de
i+1.
• Ara,
saldo
és
1.
• Ara,
saldo
és
3.
• Posem
el
valor
de
saldo
en
la
variable
i.
• Posem
el
valor
de
saldo
en
la
variable
i.
• La
variable
saldo
val
1,
per
tant,
i
ara
és
1.
• La
variable
saldo
val
3,
per
tant,
i
ara
és
3.
• Actualitzem
saldo
amb
el
resultant
de
i+1.
[Ara
el
fil
B
és
enviat
de
tornada
cap
a
runnable;
• Ara
saldo
val
2.
abans
de
que
posi
el
valor
de
saldo
a
4].
3 El
fil
A
arrenca
de
nou
a
parMr
del
4 El
fil
B
arrenca
de
nou
a
parMr
del
punt
on
ho
havia
deixat:
punt
on
ho
havia
deixat:
• Posem
el
valor
de
saldo
en
la
variable
i.
• Posem
el
valor
de
saldo
en
la
variable
i.
• La
variable
saldo
és
3,
per
tant,
i
ara
és
3.
• Ara,
saldo
és
4.
• Actualitzem
saldo
amb
el
resultat
de
i+1.
• Ara,
saldo
és
4.
Ep!
El
fil
A
el
va
actualitzar
a
5,
• Posem
el
valor
de
saldo
en
la
variable
i.
però
ara
ha
tornat
el
B
al
• La
variable
saldo
val
4,
per
tant,
i
ara
és
4.
començament
de
l’actualització
• Actualitzem
saldo
amb
el
resultant
de
i+1.
de
l’A
com
si
aquest
no
hagués
• Ara
saldo
val
5.
fet
res!!
27. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
3:
El
temut
problema
de
l’actualització
perduda:
Solució
Sincronitzant
el
mètode
increment
()
solucionem
el
problema
de
l’actualització
perduda”
perquè
manté
els
dos
passos
en
el
mètode
com
una
única
i
sòlida
unitat.
public
void
increment
()
{
int
i
=
saldo;
saldo
=
I
+
1;
}
}
28. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
4:
Interferència
entre
fils
class Counter
{
private int c = 0;
public void increment() { c++; }
public void decrement() { c--; } Dos fils A i B poden espatllar-ho:
public int valor() { return c; }
} Fil A: Recuperar c (0)
Fil B: Recuperar c (0)
c++
està
compost
de:
Fil A: Increment c (1)
1) Obtenir
el
valor
de
c
Fil B: Decrement c (-1)
2) Incrementar
c
en
1
Fil A: Emmagatzemar c (1)
3) Emmagatzemar
el
valor
de
c
Fil B: emmagatzemar c (-1)
c-‐-‐
està
compost
de:
4) Obtenir
el
valor
de
5) Decrementar
c
en
1
6) Emmagatzemar
el
valor
de
c
29. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
4:
Interferència
entre
fils
class Counter
{
private int c = 0;
public synchronized void increment() { c++; }
public synchronized void decrement() { c--; }
public int valor() { return c; }
}
30. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Exemple
3:
El
temut
problema
de
l’actualització
perduda
1 El
fil
A
funciona
durant
un
temps:
2 El
fil
B
és
seleccionat
per
funcionar:
• Prova
d’accedir
al
mètode
increment
().
• Prova
d’accedir
al
mètode
increment
().
• El
mètode
està
sincronitzat;
agafa
la
clau
Com
que
el
mètode
està
sincronitzat,
per
aquest
objecte.
necessitem
agafar
la
clau.
• Posem
el
valor
de
saldo
en
la
variable
i.
• La
clau
no
es
troba
disponible.
• La
variable
saldo
és
0
i,
ara
i
és
0.
[Ara
el
fil
B
és
enviat
a
una
“sala
de
cadenats
• Posem
a
saldo
el
resultat
de
i
+
1.
d’objecte
no
disponible”].
• Ara
saldo
val
1.
• Retornem
la
clau
(això
completa
el
mètode
3 El
fil
B
arrenca
de
nou
a
parMr
del
punt
on
increment
().
ho
havia
deixat
(encara
té
la
clau):
• Tornem
a
entrar
en
el
mètode
increment
()
i
• Posa
a
saldo
el
resultat
de
i+1.
agafem
la
clau.
• Ara,
saldo
és
2.
• Posem
el
valor
de
saldo
en
la
variable
i.
• Retorna
la
clau.
• Ara
saldo
val
1;
per
tant,
ara
i
val
1.
[Ara
el
fil
A
és
retornat
a
runnable
però,
com
[Ara
el
fil
A
és
retornat
a
runnable,
però
com
que
ja
ha
completat
el
mètode
increment
(),
el
que
no
ha
completat
el
mètode
sincronitzat,
fil
no
manté
la
clau].
manté
la
clau]
4 El
fil
B
és
seleccionat
per
funcionar:
• Prova
d’accedir
al
mètode
increment
().
El
mètode
està
sincronitzat.
Necessitem
la
clau.
• Aquest
cop,
la
clau
està
disponible.
L’agafem.
• Posem
el
valor
de
saldo
en
la
variable
i.
[ConMnua
funcionant..
]
31. És
una
bona
idea
sincronitzar-‐ho
tot?
Per
si
de
cas..
• No;
els
mètodes
sincronitzats
comporten
despeses
generals.
Quan
el
codi
arriba
a
un
mètode
sincronitzat,
hi
ha
un
impacte
en
el
rendiment
–
normalment,
no
es
percep
–
mentre
es
confirma
si
“la
clau
del
cadenat”
està
disponible.
• Un
mètode
pot
alenMr
el
programa
per
les
restriccions
que
defineix.
Un
mètode
sincronitzat
força
a
la
resta
de
fils
a
estar
disponibles
i
esperar
el
seu
torn.
• Els
mètodes
sincronitzats
poden
donar
lloc
a
un
punt
mort.
Una
regla
d’or
a
seguir
és
sincronitzar
només
el
mínim
del
que
ha
d'estar
sincronitzat.
32. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Una
regla
d’or
a
seguir
és
fesFeina
()
no
necessita
sincronitzar
només
el
mínim
ser
sincronitzat;
per
del
que
ha
d'estar
tant,
no
sincronitzarem
public
void
go
()
{
tot
el
mètode
sencer.
sincronitzat.
fesFeina
();
synchronized(this)
{
Ara,
només
aquestes
dues
crides
als
feinaCriMca
();
mètodes
s’agrupen
en
una
unitat
mesFeinaCriMca
();
atòmica.
Quan
s'uMlitza
la
paraula
clau
synchronized
dins
d'un
mètode,
enlloc
}
d'una
declaració
de
mètode,
hem
de
}
proporcionar
un
argument
que
és
l'objecte
la
clau
del
qual
el
fil
ha
d’aconseguir.
Tot
i
que
hi
ha
altres
maneres
per
fer-‐ho,
quasi
sempre
sincronitzarem
l’objecte
actual
(this).
De
fet,
és
el
mateix
objecte
que
tancariem
(lock)
si
sincronitzessim
el
mètode
sencer.
34. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Codi
sincronitzat
• Enlloc
de
sincronitzar
tot
un
mètode
podem
sincronitzar
una
porció
de
codi.
public void addName(String name)
{
synchronized(this);
{
lastName=name;
nameCount++;
}
nameList.add(name);
}
35. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• Java
uMlitza
monitors
per
la
sincronització
de
fils.
• Tot
objecte
amb
mètodes
és
un
monitor.
• El
monitor
solament
permet
a
un
fil
per
ocasió
executar
un
mètode
sobre
l’objecte.
• Los
mètodes
que
hagin
d’accedir
al
mateix
objecte,
s’han
de
declarar
per
aconseguir
el
bloqueig.
• L’execució
de
dos
mètodes
sincronitzats
és
mútuament
excloent.
37. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
//Aquest programa no està sincronitzat. class Sincro {
class Crida { public static void main(String[] args){
void crit (String msg){
Crida objectiu = new Crida();
System.out.print("["+msg);
ElQueCrida ob1 = new ElQueCrida(objectiu, "Hola");
try { ElQueCrida ob2 = new ElQueCrida(objectiu, "Mon");
Thread.sleep(1000);
ElQueCrida ob3 = new
}catch(InterruptedException e) ElQueCrida(objectiu, "Sincronitzat");
{;}
System.out.print("]"); //Esperem a que els fils acabin
} try {
} ob1.t.join();
ob2.t.join();
ob3.t.join();
public class ElQueCrida extends Thread { }catch(InterruptedException e){;}
String msg; }
Crida objectiu; }
Thread t;
public ElQueCrida(Crida objectiu, String msg) {
this.objectiu = objectiu;
this.msg = msg;
t = new Thread(this); On
haig
d’aplicar
el
monitor?
t.start();
}
public void run() {
objectiu.crit(msg);
}
}
38. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Sincronització:
Monitors
–
Exemple
//Aquest programa no està sincronitzat. class Sincro {
class Crida { public static void main(String[] args){
void crit (String msg){
Crida objectiu = new Crida();
System.out.print("["+msg);
ElQueCrida ob1 = new ElQueCrida(objectiu, "Hola");
try { ElQueCrida ob2 = new ElQueCrida(objectiu, "Mon");
Thread.sleep(1000);
ElQueCrida ob3 = new
}catch(InterruptedException e) ElQueCrida(objectiu, "Sincronitzat");
{;}
System.out.print("]"); //Esperem a que els fils acabin
} try {
} ob1.t.join();
ob2.t.join();
ob3.t.join();
public class ElQueCrida extends Thread { }catch(InterruptedException e){;}
String msg; }
Crida objectiu; }
Thread t;
public ElQueCrida(Crida objectiu, String msg) {
this.objectiu = objectiu;
this.msg = msg; On
haig
d’aplicar
el
monitor?
t = new Thread(this);
class Crida {
t.start(); synchronized void crit(String msg){
} //…
public void run() { }
objectiu.crit(msg);
}
}
39. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
§ (Fins ara..)
Casos/Exemples
amb
fils
asíncrons
i
independents
§ Cada
fil
conté
totes
les
dades
i
els
mètodes
necessaris
i
no
requereix
recursos
externs.
§ Els
fils
s’executen
en
el
seu
propi
espai;
no
els
influeix
l’estat
o
l’acMvitat
d’altres
fils
que
s’execuMn
de
forma
concurrent.
§ (Noves situacions)
Fins
concurrents
que
comparteixen
dades
i
han
de
considerar
l’estat
i
l’acMvitat
d’altres
fils.
§ Escenaris
“productor/consumidor”.
40. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
§ Escenari
“productor
–
consumidor”
§ El
PRODUCTOR
genera
un
canal
de
dades
que
és
consumit
pel
CONSUMIDOR.
Productor
Consumidor
41. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• Podem
imaginar
una
aplicació
JAVA
on
un
fil
(PRODUCTOR)
escriu
dades
en
un
arxiu
mentre
que
un
segon
fil
(CONSUMIDOR)
llegeix
les
dades
del
mateix
arxiu.
• si
teclegem
caràcters
en
el
teclat,
el
fil
PRODUCTOR
situa
les
polsacions
en
una
pila
d’esdeveniments
i
el
fil
CONSUMIDOR
llegeix
els
esdeveniments
de
la
mateixa
pila.
• Aquests
dos
exemples
uMlitzen
fils
concurrents
que
comparteixen
un
recurs
comú;
el
primer
comparteix
un
arxiu
i
el
segon
una
pila
d’esdeveniments.
• Com
que
els
fils
comparteixen
un
recurs
comú,
s’han
de
sincronitzar.
42. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
EXEMPLE
§ El
PRODUCTOR genera
un
enter
entre
0
i
9,
l’emmagatzema
en
un
objecte
i
imprimeix
el
número
generat.
§ A
més,
el
PRODUCTOR
dorm
durant
un
temps
aleatori
entre
0
i
100
milisegons
abans
de
repeMr
el
cicle
de
generació
de
números.
44. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
EXEMPLE
§ Com
que
el
CONSUMIDOR està
famèlic,
consumeix
tots
els
enters
de
l’objecte
tant
ràpid
com
sigui
possible
–
quan
el
número
generat
està
disponible,
l’agafa
–.
Productor
Consumidor
46. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
§ El
PRODUCTOR i
el CONSUMIDOR comparteixen
dades
per
mitjà
d’un
objecte
en
comú
( ).
§ Cap
dels
dos
fa
cap
esforç
per
assegurar-‐se
de
que
el
CONSUMIDOR
obMngui
cada
valor
produït
un
únic
cop.
§ La
sincronització
entre
aquests
dos
fils
es
dóna
en
un
nivell
inferior,
dins
dels
mètodes
get()
i
put()
de
l’objecte
.
48. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
I
si
els
fils
no
esMguessin
sincronitzats?
• Un
problema
seria
quan
el
PRODUCTOR
fos
més
ràpid
que
el
CONSUMIDOR
i
generés
dos
números
abans
de
que
el
CONSUMIDOR
Mngués
una
possibilitat
de
consumir
el
primer
número.
Així,
el
CONSUMIDOR
es
saltaria
un
número.
• Part
de
la
sorMda
seria..:
.
.
.
Consumidor
#1
obté:
3
Productor
#1
posa:
4
Productor
#1
posa:
5
Consumidor
#1
obté:
5
.
.
.
49. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
I
si
els
fils
no
esMguessin
sincronitzats?
• Un
altre
problema
que
podria
aparèixer
seria
si
el
CONSUMIDOR
fos
més
ràpid
que
el
PRODUCTOR
i
consumís
el
mateix
valor
dos
o
més
cops.
• En
conseqüència,
el
CONSUMIDOR
imprimiria
el
mateix
valor
dos
cops.
• Part
de
la
sorMda
seria..:
.
.
.
Productor
#1
posa:
4
Consumidor
#1
obté:
4
Consumidor
#1
obté:
4
Productor
#1
posa:
5
.
.
.
50. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• Per
prevenir
els
problemes
que
sorgeixen
en
l ’ e x e m p l e
P R O D U C T O R / C O N S U M I D O R ,
l’emmagatzematge
d’un
nou
enter
a
CubbyHole
per
part
del
PRODUCTOR
ha
d’estar
sincronitzat
amb
la
recuperació
per
part
de
CONSUMIDOR.
• El
programa
PRODUCTOR/CONSUMIDOR
uMlitza
dos
mecanismes
diferents
per
sincronitzar
els
fils:
els
monitors
i
els
mètodes
noKfy()
i
wait().
51. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• El
programa
PRODUCTOR/CONSUMIDOR
uMlitza
dos
mecanismes
diferents
per
sincronitzar
els
fils:
els
monitors
i
els
mètodes
noKfy()
i
wait().
52. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• A
Java
s’associa
un
únic
monitor
per
cada
objecte
que
té
un
mètode
sincronitzat.
• La
classe
té
2
mètodes
sincronitzats:
ü El
mètode
put()
s’uMlitza
per
canviar
el
valor
de
CubbyHole.
ü El
mètode
get()
s’uMlitza
per
recuperar
el
valor
actual.
• És
així
com
el
sistema
associa
un
únic
monitor
amb
cada
exemplar
de
CubbyHole.
Sincronització
dels
fils
53. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
El
mateix
fil
pot
cridar
a
una
mètode
sincronitzat
d’un
objecte
pel
que
ja
té
el
monitor,
és
a
dir,
pot
tornar
a
adquirir
el
monitor.
• Sempre
que
el
PRODUCTOR
crida
al
mètode
put()
de
CubbyHole,
adquireix
el
monitor
de
l’objecte
CubbyHole,
i
així
evita
que
el
consumidor
pugui
cridar
al
mètode
get()
de
CubbyHole.
• E l
m è t o d e
w a i t ( )
a l l i b e r a
temporalment
el
monitor.
• Quan
el
mètode
put()
retorna,
el
PRODUCTOR
allibera
el
monitor
i
desbloqueja
l’objecte
CubbyHole.
54. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
El
mateix
fil
pot
cridar
a
una
mètode
sincronitzat
d’un
objecte
pel
que
ja
té
el
monitor,
és
a
dir,
pot
tornar
a
adquirir
el
monitor.
• Sempre
que
el
CONSUMIDOR
crida
al
mètode
get()
de
C u b b y H o l e ,
a d q u i r e i x
e l
monitor
d’aquest
objecte
i,
per
tant,
evita
que
el
PRODUCTOR
pugui
cridar
al
mètode
put().
55. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Els
mètodes
i
poden
ser
invocats
únicament
des
de
dins
d’un
mètode
sincronitzat
o
dins
d’un
bloc
o
una
sentència
sincronitzada.
• Mètode
El
mètode
fa
que
el
fil
actual
esperi
(probablement,
per
sempre)
fins
que
un
altre
fil
li
ho
noMfiqui
o
que
canviï
una
condició.
El
mètode
s’uMlitza
en
conjunció
amb
el
mètode
per
coordinar
l’acMvitat
de
varis
fils
que
uMlitzen
els
mateixos
recursos.
Quan
el
fil
entra
en
el
mètode
,
el
monitor
és
alliberat
automàMcament,
i
quan
el
fil
surt
del
mètode
,
s’adquereix
de
nou
el
monitor.
56. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
Els
mètodes
i
poden
ser
invocats
únicament
des
de
dins
d’un
mètode
sincronitzat
o
dins
d’un
bloc
o
una
sentència
sincronitzada.
• Mètode
El
mètode
escull
un
fil
que
està
esperant
el
monitor
posseït
pel
fil
actual
i
el
desperta.
Normalment,
el
fil
que
es
troba
a
l’espera,
capturarà
el
monitor
i
procedirà.
• Mètode
El
mètode
permet
desbloquejar
tots
els
fils
que
s’hagin
bloquejat
amb
la
invocació
del
mètode
.
Quan
envia
la
noMficació
a
tots
els
fils,
aquests
comencen
a
compeMr
per
tancar
el
candau
i,
en
conseqüència,
per
executar-‐se.
Normalment,
optem
per
uKlitzar
en
detriment
de
donat
que
no
sabem
es
pot
donar
una
situació
de
coexistència
de
varis
fils
en
el
futur.
57. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• El
fil
CONSUMIDOR
crida
al
mètode
g e t ( ) ,
p e l
q u e
e l
m è t o d e
CONSUMIDOR
posseeix
el
monitor
de
CubbyHole
durant
l’execució
del
mètode
get().
• Al
final
del
mètode
get(),
la
crida
al
mètode
noKfy()
desperta
al
fil
PRODUCTOR
que
obté
el
monitor
de
CubbyHole
i
procedeix.
• Si
existeixen
varis
fils
esperant
per
un
monitor,
el
sistema
d’execució
Java
escull
un
d’aquests
fils
sense
cap
compromís
ni
garanMa
sobre
el
fil
que
serà
escollit.
58. PROCESSOS
CONCURRENTS
I
MEMÒRIA
COMPARTIDA
• El
bucle
while
conté
la
crida
a
wait().
Aquest
mètode
espera
indefinidament
fins
que
arribi
una
noMficació
del
fil
PRODUCTOR.
• Quan
el
mètode
put()
crida
a
noKfy(),
el
CONSUMIDOR
desperta
de
l’estat
d’espera
i
conMnúa
amb
el
bucle.
• Presumiblement,
el
PRODUCTOR
ja
ha
generat
un
nou
número
i
el
métode
get()
cau
al
final
del
bucle
i
procedeix.