Title: Programaci
1Programación Concurrente en Java
- Métodos sincronizados
- Monitores y condiciones
Programación Concurrente ETSI Informática-UMA
M.M. Gallardo
2Métodos Sincronizados
- Cada objeto Java tiene asociado un lock
(cerrojo). - La palabra synchronized puede utilizarse para
señalar aquellos métodos del objeto que deben
ejecutarse en exclusión mutua. - Antes de ejecutar un método sincronizado hay que
competir para conseguir el lock del objeto al que
pertenece.
3Ejemplo Problema de los jardines
- public class Cont
- private int c 0
-
- public synchronized void inc(int i)
- c
-
-
- public synchronized int valor()
- return c
-
La ejecución de inc se realiza en exclusión mutua
4Ejemplo Problema de los jardines
- public class puerta implements Runnable
- Cont c
- public jardin(Cont c)this.c c
- public void run()
- for (int i 0 ilt20 i)
- c.inc(1)
-
La ejecución de inc se realiza en exclusión mutua
5Ejemplo Problema de los jardines
- public class UsaJardines
- public static void main(String args)
- final int N 10
- Cont c new Cont()
- puerta p new puertaN
- for (int i 0 ilt N i) pi new
puerta(c) - Thread ph new ThreadN
- for (int i 0 ilt N i) phi new
Thread(pi) - for (int i 0 ilt N i) phi.start()
- for (int i 0 ilt N i)
- tryphi.join()
- catch (InterruptedException e)
- System.out.println(c.valor())
-
Todas las hebras compiten
6Mecanismo de entrada/salida al Monitor
7- Java no asigna ninguna estructura concreta a las
hebras que están en el conjunto de entrada. La
implementación podría usar - Una FIFO
- Una LIFO
- Una FIFO basada en prioridades
8Condiciones de sincronización
- Cuando una hebra que tiene el lock de un objeto
y debe suspenderse debido a alguna condición de
sincronización, se introduce en el conjunto de
espera del objeto, llamando al método - void wait()
- Cuando una hebra ejecuta wait()
- - Libera el lock del objeto sincronizado
- - Se bloquea en el conjunto de espera
9Mecanismo de entrada/salida revisado
10Disciplina del Monitor
- Los métodos void notify(), void notifiyAll()
despiertan a una/todas las hebras del conjunto de
espera. - Java utiliza la disciplina notify-and-continue,
es decir, la hebra que hace notify continúa con
el lock del monitor. Por lo tanto, la hebra que
espera debe ejecutar un código del tipo - while (!condicion)
- try wait()
- catch (Exception e)
11Métodos sincronizadosProductor/Consumidor
- public class Buffer
- private int b
- private int tam
- private int i0
- private int j0
- private int numDatos 0
-
- public Buffer(int t)
- tam t
- b new inttam
-
- ...........
-
12Métodos sincronizadosProductor/Consumidor
- .....
- public synchronized void poner(int d) throws
InterruptedException - while (numDatos tam) wait()
- bi d
- i (i 1) tam
- numDatos
- notify()
-
- public synchronized int extraer() throws
InterruptedException - while (numDatos 0) wait()
- int aux j j (j 1) tam
- numDatos--
- notify()
- return baux
-
-
mientras el buffer está lleno/vacío esperar
13Métodos sincronizadosProductor/Consumidor
- .....
- public synchronized void poner(int d) throws
InterruptedException - while (numDatos tam) wait()
- bi d
- i (i 1) tam
- numDatos
- notify()
-
- public synchronized int extraer() throws
InterruptedException - while (numDatos 0) wait()
- int aux j j (j 1) tam
- numDatos--
- notify()
- return baux
-
-
Cambia el estado del buffer y aviso a la otra
hebra, por si acaso
14Métodos sincronizados conjunto de espera único
- Cuando se utilizan métodos sincronizados hay sólo
un conjunto de espera, en el que pueden estar
suspendidas hebras que esperan que diferentes
condiciones de sincronización sean ciertas. - Cuando una hebra ejecuta notify, puede ser que no
despierte a la hebra adecuada, por lo que habrá
que programar un despertado en cascada o utilizar
el método notifyAll().
15Múltiples productores y consumidores
Condiciones de sincronización Hay varios procesos
productores y consumidores. Todos los procesos
utilizan el buffer en exclusión mutua. Un
productor no puede escribir hasta que no hay
sitio en el buffer. Los consumidores leen todos
los datos producidos, En el mismo orden. Si el
buffer está vacío esperan.
16Múltiples productores y consumidores
-suponiendo 1 productor y 3 consumidores
numDatos 3
j
b
A
Z
M
k
f
2
3
0
0
1
0
0
0
flect
c
2
3
5
Índice para cada consumidor
17Múltiples productores y consumidores
-si el consumidor 2 consume
numDatos 3
j
b
A
Z
M
k
f
2
3
0
0
1
flect
0
0
0
fcons
c
2
3
4
3
2
0
Índice para cada consumidor
Ya no puede consumir más
18Múltiples productores y consumidores
-si el consumidor 0 consume
numDatos 2
j
b
A
Z
M
k
f
0
2
3
0
0
flect
0
0
0
fcons
c
3
3
4
2
2
0
Índice para cada consumidor
Deja un hueco libre para el productor
19Múltiples productores y consumidores
-si el productor produce
numDatos 3
j
b
H
A
Z
M
k
f
0
2
3
0
0
flect
3
0
0
fcons
c
3
3
4
3
3
1
Índice para cada consumidor
Indica a todos que pueden Consumir un dato más
20Múltiples productores y consumidores
- package multProdCons
-
- public class Buffer
- private int b // buffer
- private int c // indice de cada consumidor
- private int fdatos // para cada consumidor,
los datos que le quedan por leer - private int flect // para cada dato, las
lecturas que le quedan - private int tam
- private int i0
- private int j0
- private int numDatos 0
- private int nlectores 0
- .................
21Múltiples productores y consumidores
- public Buffer(int t,int nlectores)
- tam t
- b new inttam
- c new inttam
- for (int i0ilttami) ci 0
- this.nlectores nlectores
- fdatos new intnlectores
- for (int i0iltnlectoresi)
- fdatosi 0
- flect new inttam
- for (int i 0 i lt tam i)
- flecti 0
- System.out.println("buffer inicializado")
22Múltiples productores y consumidores
- public synchronized void poner(int id,int d)
throws InterruptedException - while (numDatos tam) notify() wait()
- bi d
- for (int c 0 c lt nlectores c)
fdatosc - flecti nlectores
- i (i 1) tam
- numDatos
- notify()
-
En la sala de espera hay procesos productores y
consumidores. Cuando se despierta una hebra,
puede ser que tenga que seguir esperando....
Desperado en cascada
23Múltiples productores y consumidores
- public synchronized int extraer(int id) throws
InterruptedException - while (fdatosid 0) notify() wait()
- int aux cid
- fdatosid--
- flectcid--
- if (flectcid 0) numDatos-- notify()
- cid (cid 1) tam
- return baux
-
Como en el caso de los productores
Desperado en cascada
24Múltiples productores y consumidores
- public class Consumidor implements Runnable
- Buffer b
- int id
- public Consumidor(Buffer b, int id)
- this.b b
- this.id id
-
- public void run()
- int d 0
- System.out.println("comienza consumidor")
- for (int i0ilt10i)
- tryd b.extraer(id)
- catch (Exception e)
- System.out.println("Consumidor " d)
-
-
25Múltiples productores y consumidores
- public class Productor implements Runnable
- Buffer b
- int id
- public Productor(Buffer b,int id)
- this.b b
- this.id id
-
-
- public void run()
- System.out.println("comienza productor")
- for (int i 0 ilt10 i)
- try b.poner(id,i)
- catch (Exception e)
- System.out.println("Productor "i)
-
-
26Múltiples productores y consumidores
- public class UsaProdCons
-
- public static void main(String args)
- final int numCons 5
- Buffer b new Buffer(5,numCons)
- Productor p new Productor(b,1)
- Consumidor c new ConsumidornumCons
- for (int i 0 iltnumCons i) ci new
Consumidor(b,i) - System.out.println("comienza el programa")
- Thread ph new Thread(p)
- Thread ch new ThreadnumCons
- for (int i 0 iltnumCons i) chi new
Thread(ci) - for (int i 0 iltnumCons i) chi.start()
- ph.start()
-
27Múltiples productores y consumidores con notifyAll
- public synchronized void poner(int id,int d)
throws InterruptedException - System.out.println("productor " id "quiere
poner "d) - while (numDatos tam) wait()
- System.out.println("productor " id "
escribe en el buffer") - bi d
- for (int c 0 c lt nlectores c)
fdatosc - flecti nlectores
- i (i 1) tam
- numDatos
- notifyAll()
-
-
Hay un nuevo dato y aviso a todos
28Lectores/Escritores (v. injusta)
- public class ControlBD
- private int nLectores 0
- private boolean escribiendo false
-
- public synchronized void OpenL(int i) throws
Exception - while (escribiendo) wait()
- nLectores
- System.out.println("Entra lector "i)
-
-
- public synchronized void OpenE(int i) throws
Exception - while (escribiendo (nLectores gt 0)) wait()
- escribiendo true
- System.out.println("Entra escritor "i)
-
- ......
-
29Lectores/Escritores (v. injusta)
- public synchronized void CloseL(int i) throws
Exception - nLectores--
- if (nLectores 0) notifyAll()
- System.out.println("Sale lector "i)
-
-
- public synchronized void CloseE(int i) throws
Exception - escribiendo false
- notifyAll()
- System.out.println("Sale escritor "i)
-
-
-
30Lectores/Escritores (v. injusta)
- public class Escritor implements Runnable
- int miId
- ControlBD c
- public Escritor(int id,ControlBD c)
- miId id this.c c
-
- public void run()
- for (int i 0 i lt 10 i)
- try c.OpenE(miId)
- Thread.sleep(1)
- c.CloseE(miId)
-
- catch (Exception e)
-
-
-
31Lectores/Escritores (v. injusta)
- public class Lector implements Runnable
- int miId
- ControlBD c
- public Lector(int id,ControlBD c)
- miId idthis.c c
-
- public void run()
- for (int i 0 i lt 10 i)
- try c.OpenL(miId)
- Thread.sleep(1)
- c.CloseL(miId)
-
- catch (Exception e)
-
-
32Lectores/Escritores
- El tener solo una cola de espera puede ser
ineficiente si despertamos a procesos que no
pueden continuar su ejecución porque todavía no
se satisface la condición por la que esperan.
33Lectores/Escritores (v. justa)
- public class ControlBD
- private int nLectores 0
- private boolean escribiendo false
- private int nEscritores 0
- public synchronized void OpenL(int i) throws
Exception - while (escribiendo (nEscritores gt 0))
- System.out.println("Lector quiere entrar "i)
- wait()
-
- nLectores
-
- ..............
-
-
34Lectores/Escritores (v. justa)
- public synchronized void OpenE(int i) throws
Exception - nEscritores
- while (escribiendo (nLectores gt 0))
- System.out.println("Escritor quiere entrar
"i) - wait()
-
- escribiendo true
-
-
-
35Lectores/Escritores (v. justa)
- public synchronized void CloseL(int i) throws
Exception - nLectores--
- if (nLectores 0) notifyAll()
-
-
- public synchronized void CloseE(int i) throws
Exception - nEscritores--
- escribiendo false
- notifyAll()
-
-
-
-
Despierta a todos
36Lectores/Escritores (v. justa)
- public synchronized void CloseL(int i) throws
Exception - nLectores--
- if (nLectores 0) notifyAll()
-
-
- public synchronized void CloseE(int i) throws
Exception - nEscritores--
- escribiendo false
- notifyAll()
-
-
-
-
Despierta a todos
37Lectores/Escritores (v. justa)
- comienza Lectores/Escritores
- Escritor quiere entrar 0
- Escritor quiere entrar 1
- Lector quiere entrar 0
- Lector quiere entrar 1
- Lector quiere entrar 2
- Lector quiere entrar 3
- Lector quiere entrar 4
- Lector quiere entrar 5
- Lector quiere entrar 6
- Lector quiere entrar 7
- Lector quiere entrar 8
- Lector quiere entrar 9
- Lector quiere entrar 10
- Lector quiere entrar 11
- Lector quiere entrar 12
- Lector quiere entrar 13
- Escritor quiere entrar 1
- Lector quiere entrar 0
Hasta 350 intentos fallidos para 15 Lectores y
2 Escritores
38Llamadas anidadas a métodos sincronizados
- Para que h ejecute e.p(), debe obtener el lock de
e
Ejemplo e ---
e.p()
39Llamadas anidadas a métodos sincronizados
Ejemplo2 e1 ....
Ejemplo e ---
e.p()
e1.p()
Para que e ejecute e1.p() debe obtener el lock de
e1
40Llamadas anidadas a métodos sincronizados
Ejemplo2 e1 ....
Ejemplo e ---
e.p()
e1.p()
41Llamadas anidadas a métodos sincronizados
Ejemplo2 e1 ....
Ejemplo e ---
e.p()
e1.p()
Si se ejecuta wait(), se libera el lock de
e1, pero se mantiene el de e, lo que puede
producir bloqueos
42Productor/Consumidor con condiciones y bloqueo
- public class Condition
- public synchronized void delay()
- trywait() // suspende a la hebra que lo
ejecuta - catch (Exception e)
-
-
- public synchronized void resume()
- trynotify() // despierta una hebra suspendida
- catch (Exception e)
-
43Productor/Consumidor con condiciones y bloqueo
- public class Buffer
- private int b
- private int tam
- private int i0
- private int j0
- private int numDatos 0
-
-
- private final Condition nolleno new
Condition() - private final Condition novacio new
Condition() -
- public Buffer(int t)
- tam t
- b new inttam
-
- .......
-
44Productor/Consumidor con condiciones y bloqueo
- public synchronized void poner(int d) throws
InterruptedException - while (numDatos tam) nolleno.delay()
- bi d
- i (i 1) tam
- numDatos
- novacio.resume()
-
-
- public synchronized int extraer() throws
InterruptedException - while (numDatos 0) novacio.delay()
- int aux j
- j (j 1) tam
- numDatos--
- nolleno.resume()
- return baux
-
Bloquea a la hebra Buffer
Bloquea a la hebra Buffer
45Locks
- Los métodos/instrucciones synchronized modelan el
acceso exclusivo a lock de un monitor implícito
asociado a un objeto, típicamente un recurso
compartido por varias hebras. - Sin embargo, cuando una hebra necesita usar más
de un recurso, debe liberar los locks de los
recursos en orden inverso a como se han obtenido,
lo que en ocasiones puede no ser adecuado - ....
- A.acquire()
- B.acquire()
- C.acquire()
- A.release()
- D.acquire()
- B.release() ......
46Locks
- public class ReentrantLock
- ReentrantLock l new ReentrantLock()
- Un Lock para la exclusión mutua con la misma
semántica y comportamiento que el lock implícito
tipo monitor de los métodos e instrucciones
sincronizadas (synchronized), pero con más
posibilidades.
47Locks el problema de los jardines
Implementación de la interfaz lock
- public class Cont
- Lock l new ReentrantLock()
- private int c 0
-
- public void inc(int i)
- l.lock()
- try
- c
- finally
- l.unlock()
-
-
-
Pido el lock
Devuelvo el lock
48Locks el problema de los jardines
- public int valor()
- l.lock()
- try
- return c
- finally
- l.unlock()
-
-
-
-
Pido el lock
Devuelvo el lock
La cláusula try/finally es necesaria para
devolver el lock después de ejecutar return
49Locks condiciones
Para modelar las condiciones de sincronización
usamos la Interfaz Condition.
- public interface Condition
- Las condiciones clasifican los métodos del
monitor (wait, notify and notifyAll) en distintos
objetos de forma que es posible tener múltiples
conjuntos de espera por objeto, asociados a
locks. - Lock l new ReentrantLock()
- Condition c1 l.newCondition()
- Condition c2 l.newCondition()
Las condiciones se asocian a locks y pueden
definirse tantas como sea necesario
50Locks condiciones
- Métodos
- void await() throws InterruptedException
- Suspende a la hebra en la condición
correspondiente - void signal()
- Despierta una de las hebras que espera. La hebra
tiene que conseguir el lock correspondiente antes
de continuar su ejecución. - (disciplina signal-and-continue)
- void signalAll()
- Desperta a todas las hebras que esperan. Cada
hebra tiene que conseguir el lock correspondiente
antes de continuar su ejecución. - (disciplina signal-and-continue)
51Locks condiciones
- Lock l new ReentrantLock()
- Condition c1 l.newCondition()
- Condition c2 l.newCondition()
- l.lock()
- try
- while (!condicion1) c1.await() // mientras
!condicion1 espera en c1 - // condicion1 se satisface
- // cambia el estado del objeto y condicion2 es
cierta - c2.signal() // despertar una hebra que espera
- finally
- l.unlock()
52Productor/Consumidor
- package condicion
- import java.util.concurrent.locks.
- public class Buffer
- private int b
- private int tam
- private int i0
- private int j0
- private int numDatos 0
-
- private final ReentrantLock lockBuffer new
ReentrantLock() -
- private final Condition nolleno
lockBuffer.newCondition() - private final Condition novacio
lockBuffer.newCondition() -
- public Buffer(int t)
- tam t
- b new inttam
-
- .........
53Productor/Consumidor
- public void poner(int d) throws
InterruptedException - System.out.println("productor quiere poner
"d) - try
- lockBuffer.lock()
- while (numDatos tam) nolleno.await()
- bi d
- i (i 1) tam
- numDatos
- novacio.signal()
- finallylockBuffer.unlock()
-
-
-
54Productor/Consumidor
- public int extraer() throws InterruptedException
- System.out.println("consumidor quiere extraer"
numDatos) - try
- lockBuffer.lock()
- while (numDatos 0) novacio.await()
- int aux j
- j (j 1) tam
- numDatos--
- nolleno.signal()
- return baux
- finally lockBuffer.unlock()
-
55Barbero Dormilón
- import java.util.concurrent.locks.
- public class Barberia
-
- private Lock BLock new ReentrantLock()
-
- private Condition cBlibre BLock.newCondition()
- private Condition cSillaOcupada
BLock.newCondition() - private Condition cPuertaAbierta
BLock.newCondition() - private Condition cSiguiente
BLock.newCondition() -
- private boolean Blibre false
- private boolean SillaOcupada false
- private boolean PAbierta false
-
56Barbero Dormilón
- public void siguiente()
- BLock.lock()
- try
- System.out.println("Barbero libre")
- Blibre true
- cBlibre.signal()
- while (!SillaOcupada)
- trycSillaOcupada.await()
- catch (InterruptedException e)
-
- finally
- BLock.unlock()
-
-
-
-
57Barbero Dormilón
- public void finPelar()
- BLock.lock()
- try
- PAbierta true
- cPuertaAbierta.signal()
- while (PAbierta)
- trycSiguiente.await()
- catch (InterruptedException e)
-
- finally
- BLock.unlock()
-
-
-
-
58Barbero Dormilón
- public void qPelar(int i)
- BLock.lock()
- try
- while (!Blibre)
- trycBlibre.await()
- catch (InterruptedException e)
- Blibre false
- SillaOcupada true
- System.out.println("Cliente "i" se sienta en
la silla") - cSillaOcupada.signal()
- while (!PAbierta)
- trycPuertaAbierta.await()
- catch (InterruptedException e)
- System.out.println("Cliente "i" se va")
- PAbierta false
- cSiguiente.signal()
- finally
- BLock.unlock()
-
59Barbero Dormilón
- public void qPelar(int i)
- BLock.lock()
- try
- while (!Blibre)
- trycBlibre.await()
- catch (InterruptedException e)
- Blibre false
- SillaOcupada true
- System.out.println("Cliente "i" se sienta en
la silla") - cSillaOcupada.signal()
- while (!PAbierta)
- trycPuertaAbierta.await()
- catch (InterruptedException e)
- System.out.println("Cliente "i" se va")
- PAbierta false
- cSiguiente.signal()
- finally
- BLock.unlock()
-
60Barbero Dormilón
- public class Barbero implements Runnable
- Barberia b
- public Barbero(Barberia b)
- this.b b
-
-
- public void run()
- while (true)
- b.siguiente()
- System.out.println("Barbero Pela Cliente")
- // Barbero pela cliente
- b.finPelar()
-
-
-
-
61Barbero Dormilón
- public class cliente implements Runnable
- Barberia b
- int id
- public cliente(Barberia b,int i)
- this.b b
- id i
-
-
- public void run()
- b.qPelar(id)
-
-
-
62Barbero Dormilón
- public class UsaBarberia
- public static void main(String args)
- final int N 125
- Barberia b new Barberia()
- Barbero bar new Barbero(b)
- cliente c new clienteN
-
- for (int i 0 iltN i)
- ci new cliente(b,i)
-
- Thread bh new Thread(bar)
- bh.start()
- Thread ch new ThreadN
-
- for (int i 0 iltN i)
- chi new Thread(ci)
-
- for (int i 0 iltN i)