20151028
dit
UPM
Programación concurrente —(cid:1)
Sincronización condicional
Juan Antonio de la Puente
<
[email protected]>
Algunos derechos reservados. Este documento se distribuye bajo licencia
Crea9ve Commons Reconocimiento-NoComercial-Compar9rIgual 3.0 Unported.
hBp://crea9vecommons.org/licenses/by-nc-sa/3.0/deed.es
Referencias
•ScoB Oaks & Henry Wong
Java Threads
O'Reilly Media; 3rd ed (2004)
•Kathy Sierra & Bert Bates
Head First Java, ch. 15
O'Reilly Media; 2nd ed (2005)
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
2
Sincronización condicional
Sincronización condicional
• A veces una hebra 9ene que esperar hasta que se cumpla
una determinada condición
‣ suspende su ejecución hasta que esto ocurra
• Otra hebra hace que se cumpla la condición
‣ avisa al que estaba esperando
‣ éste puede reanudar su ejecución
• Este 9po de sincronización se llama sincronización
condicional
‣ sincronización: el avance de una hebra depende de lo que haga otra
hebra
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
4
Ejemplo: ges9ón de un estacionamiento
• Las hebras de las clase Sensor avisan cuando entra o sale
un coche
«thread»
Sensor
- acceso, id
«thread»
Supervisor
«monitor»
Parking
- n : natural
+ entra
+ sale
+ ocupado: natural
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
5
Sincronización
• No deben entrar coches si el estacionamiento está lleno
‣ no se puede entrar si n >= capacidad
‣ si un sensor llama a entra cuando n >= capacidad, debe suspender
su ejecución hasta que haya si9o
- está implícito que en ese caso no se abre la barrera hasta que se
pueda con9nuar
Programación concurrente — Exclusión mutua
© 2014 Juan A. de la Puente
6
Ejemplo: productor y consumidor
• Una hebra produce elementos de un cierto 9po
• Otra los consume
• Cada una avanza con un ritmo diferente
• Se usa un almacenamiento intermedio (buffer)
‣ el productor los va poniendo en el buffer según los produce
‣ el consumidor los saca del buffer cuando los necesita
«thread»
Productor
«thread»
Consumidor
«monitor»
Buffer
+ envia
+ recibe
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
7
Sincronización
• No se pueden extraer datos si el buffer está vacío
‣ si cuando el consumidor invoca recibe no hay datos en el buffer,
debe suspender su ejecución hasta que los haya
‣ es el productor el que los pone
• No se pueden añadir datos si el buffer está lleno
‣ si cuando el productor invoca envía el buffer está lleno,
debe suspender su ejecución hasta que haya si9o
‣ es el consumidor el que hace si9o al extraer datos
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
8
Esperar y avisar
Espera condicional
• El método wait() suspende la ejecución de la hebra
que lo invoca
• Se usa para esperar una condición
while (!condición)
wait();
‣ ¡siempre en un bucle!
• Sólo se puede invocar dentro de un método sincronizado
‣ o de un bloque sincronizado
• Se libera el cerrojo del objeto atómicamente
‣ al mismo 9empo que se hace wait()
• Puede lanzar una excepción InterruptedExcep;on
‣ usar manejador o propagar
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
10
Aviso de condición
• El método notify() reanuda la ejecución de una
hebra que esté suspendida por haber hecho wait()
• Se usa para avisar de que se cumple una condición
// cambiar el estado de alguna condición
notify();
• Una hebra avisa a otra
• Sólo se puede invocar dentro de un método sincronizado
‣ o de un bloque sincronizado
• No se libera el cerrojo del objeto hasta que termina el
método o sentencia sincronizada
‣ la hebra que se reanuda intenta adquirir el cerrojo para con9nuar
‣ puede tener que compe9r con otras hebras
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
11
Aviso general
• El método notifyAll() reanuda la ejecución de
todas las hebras que esté suspendidas por haber hecho
wait() en el mismo objeto
• El cerrojo del objeto se libera cuando termina el método
o sentencia sincronizada
‣ todas las hebras que se reanudan compiten para adquirir el cerrojo
y poder con9nuar
• Con notify() no se sabe qué hebra se reanuda entre
las que estaban suspendidas
• Con notifyAll() se reanudan todas y comprueban
otra vez la condición
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
12
Colas de espera y de entrada
adquirir el cerrojo
cerrojo
wait
cola de entrada
cola de espera
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
13
no9fy vs no9fyAll
• Siempre que varias hebras puedan estar esperando
condiciones dis9ntas hay que usar notifyAll
• Sólo conviene hacer notify si
‣ todas las hebras esperan la misma condición
‣ sólo una hebra puede avanzar cuando se cumple la condición
• En caso de duda, usar notifyAll
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
14
Ejemplo: estacionamiento
Monitor con condiciones
public class Parking {
private final int capacidad; // número de coches que caben
private int n = 0; // número de coches que hay
this.capacidad = capacidad;
while (n >= capacidad) wait();
n++;
// constructor
public Parking (int capacidad) {
}
// entra un coche por una de las puertas
public synchronized void entra (String puerta) {
}
// sale un coche por una de las puertas
public synchronized void sale (String puerta) {
}
// consulta
public synchronized int ocupado() {
}
n—;
notifyAll();
return n;
}
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
16
Productor y consumidor
Ejemplo: buffer
public class Buffer<E> {
private E almacen;
private boolean lleno = false;
public synchronized void enviar(E dato)
throws InterruptedException {
while (lleno) wait(); // espera que haya sitio
almacen = dato;
lleno = true;
notifyAll(); // avisa de que hay un valor
}
public synchronized E recibir()
throws InterruptedException {
}
E dato = null;
while (!lleno) wait(); // espera que haya un valor
dato = almacen;
lleno = false;
notifyAll(); // avisa de que hay sitio
return dato;
}
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
18
Ejemplo: productor
public class Productor<E> implements Runnable {
private Buffer<E> b;
public Productor(Buffer<E> b) {
}
this.b = b;
public void run() {
while (true) {
}
}
E x = …; // producir x
b.enviar(x);
}
// la sincronización está oculta en el buffer
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
19
Ejemplo: consumidor
public class Consumidor<E> implements Runnable {
private Buffer<E> b;
public Consumidor(Buffer<E> b) {
}
this.b = b;
public void run() {
while (true) {
}
}
E x = b.recibir();
… // consumir x
}
// la sincronización está oculta en el buffer
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
20
Resumen
Resumen
• La sincronización condicional se realiza en Java con los
métodos wait() y no9fy() / no9fyAll()
‣ sólo se pueden usar en métodos/bloques sincronizados
‣ wait() suspende la hebra desde donde se invoca
‣ no9fy() reanuda una hebra suspendida
‣ no9fyAll() reanuda todas las hebras suspendidas
• Hay que hacer wait() siempre en un bucle while
‣ para comprobar otra vez la condición de espera cuando despierta
while (!condición) wait();
Programación concurrente — Sincronización condicional
© 2014 Juan A. de la Puente
22
Comentarios de: Programación concurrente - Sincronización condicional (0)
No hay comentarios