Programación Concurrente en Java

62
Programación Concurrente en Java Métodos sincronizados Monitores y condiciones Programación Concurrente ETSI Informática-UMA M.M. Gallardo

description

Programación Concurrente en Java. Métodos sincronizados Monitores y condiciones. Programación Concurrente ETSI Informática-UMA M.M. Gallardo. Métodos Sincronizados. Cada objeto Java tiene asociado un lock (cerrojo). - PowerPoint PPT Presentation

Transcript of Programación Concurrente en Java

Page 1: Programación Concurrente  en Java

Programación Concurrente en Java

Métodos sincronizados

Monitores y condiciones

Programación Concurrente ETSI Informática-UMA M.M. Gallardo

Page 2: Programación Concurrente  en Java

Mé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.

Page 3: Programación Concurrente  en Java

Ejemplo: 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 serealiza en exclusión mutua

Page 4: Programación Concurrente  en Java

Ejemplo: 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; i<20; i++)

c.inc(1);

}

}

La ejecución de inc serealiza en exclusión mutua

Page 5: Programación Concurrente  en Java

Ejemplo: Problema de los jardines

public class UsaJardines {public static void main(String[] args){

final int N = 10;Cont c = new Cont();puerta[] p = new puerta[N];for (int i = 0; i< N; i++) p[i] = new puerta(c);Thread[] ph = new Thread[N];for (int i = 0; i< N; i++) ph[i] = new Thread(p[i]);for (int i = 0; i< N; i++) ph[i].start();for (int i = 0; i< N; i++)

try{ph[i].join();}catch (InterruptedException e){}

System.out.println(c.valor());}

}

Todas las hebrascompiten

Page 6: Programación Concurrente  en Java

Mecanismo de entrada/salida al Monitor

Page 7: Programación Concurrente  en Java

• 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

Page 8: Programación Concurrente  en Java

Condiciones 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

Page 9: Programación Concurrente  en Java

Mecanismo de entrada/salida revisado

Page 10: Programación Concurrente  en Java

Disciplina 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){}

Page 11: Programación Concurrente  en Java

Métodos sincronizados:Productor/Consumidor

public class Buffer {private int[] b;private int tam;private int i=0;private int j=0;private int numDatos = 0;

public Buffer(int t){tam = t;b = new int[tam];

}...........

}

Page 12: Programación Concurrente  en Java

Métodos sincronizados:Productor/Consumidor

.....public synchronized void poner(int d) throws InterruptedException{

while (numDatos == tam) wait();b[i] = 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 b[aux];}

}

mientras el buffer está lleno/vacíoesperar

Page 13: Programación Concurrente  en Java

Métodos sincronizados:Productor/Consumidor

.....public synchronized void poner(int d) throws InterruptedException{

while (numDatos == tam) wait();b[i] = 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 b[aux];}

}

Cambia el estado del buffer yaviso a la otra hebra, por si acaso

Page 14: Programación Concurrente  en Java

Mé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().

Page 15: Programación Concurrente  en Java

Múltiples productores y consumidores

Condiciones de sincronizaciónHay varios procesos productores y consumidores.Todos los procesos utilizan el buffer en exclusión mutua.Un productor no puede escribir hasta que no hay sitioen el buffer.Los consumidores leen todos los datos producidos,En el mismo orden. Si el buffer está vacío esperan.

Page 16: Programación Concurrente  en Java

Múltiples productores y consumidores

flect

j

b AZMkf

-suponiendo 1 productor y 3 consumidores

c 2 3 5

0 0 1 2 3

Índice para cadaconsumidor

numDatos = 3

0 0 0

Page 17: Programación Concurrente  en Java

Múltiples productores y consumidores

flect

j

b AZMkf

-si el consumidor 2 consume

c 2 3 4

0 0 1 2 3

Índice para cadaconsumidor

fcons3 2 0

numDatos = 3

0 0 0

Ya no puede consumir más

Page 18: Programación Concurrente  en Java

Múltiples productores y consumidores

flect

j

b AZMkf

-si el consumidor 0 consume

c 3 3 4

0 0 0 2 3

Índice para cadaconsumidor

fcons2 2 0

numDatos = 2

0 0 0

Deja un hueco libre para el productor

Page 19: Programación Concurrente  en Java

Múltiples productores y consumidores

flect

j

b AZMkf

-si el productor produce

c 3 3 4

0 0 0 2 3

Índice para cadaconsumidor

fcons3 3 1

numDatos = 3

3 0 0

Indica a todos que puedenConsumir un dato más

H

Page 20: Programación Concurrente  en Java

Múltiples productores y consumidores

package multProdCons; public class Buffer {

private int[] b; // bufferprivate int[] c; // indice de cada consumidorprivate int[] fdatos; // para cada consumidor, los datos que le quedan por leerprivate int[] flect; // para cada dato, las lecturas que le quedanprivate int tam;private int i=0;private int j=0;private int numDatos = 0;private int nlectores = 0;

.................

}

Page 21: Programación Concurrente  en Java

Múltiples productores y consumidores

public Buffer(int t,int nlectores){tam = t;b = new int[tam];c = new int[tam];for (int i=0;i<tam;i++) c[i] = 0;this.nlectores = nlectores;fdatos = new int[nlectores];for (int i=0;i<nlectores;i++)

fdatos[i] = 0;flect = new int[tam];for (int i = 0; i < tam ; i++)

flect[i] = 0;System.out.println("buffer inicializado");

}

Page 22: Programación Concurrente  en Java

Múltiples productores y consumidores

public synchronized void poner(int id,int d) throws InterruptedException{

while (numDatos == tam) {notify(); wait();}

b[i] = d;

for (int c = 0; c < nlectores ; c++) fdatos[c]++;

flect[i] = nlectores;

i = (i + 1) % tam;

numDatos++;

notify();

}

Desperado en cascada

En la sala de esperahay procesos productoresy consumidores. Cuando se despiertauna hebra, puede ser que tenga queseguir esperando....

Page 23: Programación Concurrente  en Java

Múltiples productores y consumidores

public synchronized int extraer(int id) throws InterruptedException{

while (fdatos[id] == 0) {notify(); wait();}

int aux = c[id];

fdatos[id]--;

flect[c[id]]--;

if (flect[c[id]] == 0) {numDatos--; notify();}

c[id] = (c[id] + 1) % tam;

return b[aux];

}

Desperado en cascada

Como en el caso de losproductores

Page 24: Programación Concurrente  en Java

Mú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 i=0;i<10;i++){

try{d = b.extraer(id);} catch (Exception e){};System.out.println("Consumidor " + d);

}}

}

Page 25: Programación Concurrente  en Java

Mú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; i<10; i++){

try {b.poner(id,i);} catch (Exception e){};System.out.println("Productor "+i);

}}

}

Page 26: Programación Concurrente  en Java

Mú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 Consumidor[numCons];for (int i = 0; i<numCons; i++) c[i] = new Consumidor(b,i);System.out.println("comienza el programa");Thread ph = new Thread(p);Thread[] ch = new Thread[numCons];for (int i = 0; i<numCons; i++) ch[i] = new Thread(c[i]);for (int i = 0; i<numCons; i++) ch[i].start();ph.start(); }

}

Page 27: Programación Concurrente  en Java

Mú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");b[i] = d;for (int c = 0; c < nlectores ; c++) fdatos[c]++;

flect[i] = nlectores;i = (i + 1) % tam;numDatos++;notifyAll();

}Hay un nuevo datoy aviso a todos

Page 28: Programación Concurrente  en Java

Lectores/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 > 0)) wait();escribiendo = true;System.out.println("Entra escritor "+i);

}......

Page 29: Programación Concurrente  en Java

Lectores/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);

}}

Page 30: Programación Concurrente  en Java

Lectores/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 < 10; i++){try{ c.OpenE(miId);Thread.sleep(1);c.CloseE(miId);}catch (Exception e){};}}

}

Page 31: Programación Concurrente  en Java

Lectores/Escritores (v. injusta)

public class Lector implements Runnable{int miId;ControlBD c;public Lector(int id,ControlBD c){miId = id;this.c = c;}public void run(){for (int i = 0; i < 10; i++){try{ c.OpenL(miId);Thread.sleep(1);c.CloseL(miId);}catch (Exception e){};}}

}

Page 32: Programación Concurrente  en Java

Lectores/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.

Page 33: Programación Concurrente  en Java

Lectores/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 > 0)) {System.out.println("Lector quiere entrar "+i);wait();

}nLectores++;

}..............

Page 34: Programación Concurrente  en Java

Lectores/Escritores (v. justa)

public synchronized void OpenE(int i) throws Exception{

nEscritores++;

while (escribiendo || (nLectores > 0)) {

System.out.println("Escritor quiere entrar "+i);

wait();

}

escribiendo = true;

}

Page 35: Programación Concurrente  en Java

Lectores/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

Page 36: Programación Concurrente  en Java

Lectores/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

Page 37: Programación Concurrente  en Java

Lectores/Escritores (v. justa)comienza Lectores/EscritoresEscritor quiere entrar 0Escritor quiere entrar 1Lector quiere entrar 0Lector quiere entrar 1Lector quiere entrar 2Lector quiere entrar 3Lector quiere entrar 4Lector quiere entrar 5Lector quiere entrar 6Lector quiere entrar 7Lector quiere entrar 8Lector quiere entrar 9Lector quiere entrar 10Lector quiere entrar 11Lector quiere entrar 12Lector quiere entrar 13Escritor quiere entrar 1Lector quiere entrar 0Lector quiere entrar 1Lector quiere entrar 2Lector quiere entrar 3Lector quiere entrar 4Lector quiere entrar 6...................................

Hasta 350 intentos fallidos para

15 Lectores y 2 Escritores

Page 38: Programación Concurrente  en Java

Llamadas anidadas a métodos sincronizados

Para que h ejecute e.p(), debe obtener el lock de e

Hebra h

class Ejemplo{

public synchronized void p(){

..........

}

}

Ejemplo e = ---

e.p()

Page 39: Programación Concurrente  en Java

class Ejemplo2{

public synchronized void p(){

..........

}

}

class Ejemplo{

public synchronized void p(){

..........

}

}

Llamadas anidadas a métodos sincronizados

Hebra h tiene lock(e)

Ejemplo e = ---

e.p()

Ejemplo2 e1 = ....

e1.p()

Para que e ejecute e1.p() debe obtener el lock de e1

Page 40: Programación Concurrente  en Java

class Ejemplo2{

public synchronized void p(){

..........

}

}

class Ejemplo{

public synchronized void p(){

..........

}

}

Llamadas anidadas a métodos sincronizados

Hebra h tiene lock(e), lock(e1)

Ejemplo e = ---

e.p()

Ejemplo2 e1 = ....

e1.p()

Page 41: Programación Concurrente  en Java

class Ejemplo{

public synchronized void p(){

..........

}

}

Llamadas anidadas a métodos sincronizados

Hebra h tiene lock(e)

Ejemplo e = ---

e.p()

Ejemplo2 e1 = ....

e1.p()

class Ejemplo2{

public synchronized void p(){

.......... wait()}

}

Si se ejecuta wait(), se libera el lock de e1,pero se mantiene el de e, lo que puede producir bloqueos

Page 42: Programación Concurrente  en Java

Productor/Consumidor con condiciones y bloqueo

public class Condition {

public synchronized void delay(){

try{wait(); // suspende a la hebra que lo ejecuta

}catch (Exception e){};

}

public synchronized void resume(){

try{notify(); // despierta una hebra suspendida

}catch (Exception e){};

}

}

Page 43: Programación Concurrente  en Java

Productor/Consumidor con condiciones y bloqueo

public class Buffer {private int[] b;private int tam;private int i=0;private int j=0;private int numDatos = 0;

private final Condition nolleno = new Condition(); private final Condition novacio = new Condition();

public Buffer(int t){tam = t;b = new int[tam];

}.......}

Page 44: Programación Concurrente  en Java

Productor/Consumidor con condiciones y bloqueo

public synchronized void poner(int d) throws InterruptedException{while (numDatos == tam) nolleno.delay();b[i] = 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 b[aux];

}

Bloquea a la hebra Buffer

Bloquea a la hebra Buffer

Page 45: Programación Concurrente  en Java

Locks

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(); ......

Page 46: Programación Concurrente  en Java

Locks

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.

Page 47: Programación Concurrente  en Java

Locks: el problema de los jardines

public class Cont {Lock l = new ReentrantLock();private int c = 0;

public void inc(int i){l.lock();try{c++;} finally {

l.unlock();}

}

Implementación de la interfaz lock

Pido el lock

Devuelvo el lock

Page 48: Programación Concurrente  en Java

Locks: 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 esnecesaria para devolverel lock después de ejecutarreturn

Page 49: Programación Concurrente  en Java

Locks: condiciones

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();

Para modelar las condiciones de sincronización usamos laInterfaz Condition.

Las condiciones se asocian a locks y pueden definirse tantascomo sea necesario

Page 50: Programación Concurrente  en Java

Locks: 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)

Page 51: Programación Concurrente  en Java

Locks: 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(); }

Page 52: Programación Concurrente  en Java

Productor/Consumidorpackage condicion;import java.util.concurrent.locks.*;public class Buffer {

private int[] b;private int tam;private int i=0;private int j=0;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 int[tam];

}.........

Page 53: Programación Concurrente  en Java

Productor/Consumidor

public void poner(int d) throws InterruptedException{System.out.println("productor quiere poner "+d);try{

lockBuffer.lock();while (numDatos == tam) nolleno.await();b[i] = d;i = (i + 1) % tam;numDatos++;novacio.signal();

}finally{lockBuffer.unlock();}}

}

Page 54: Programación Concurrente  en Java

Productor/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 b[aux];

} finally {lockBuffer.unlock();}

}

Page 55: Programación Concurrente  en Java

Barbero 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;

Page 56: Programación Concurrente  en Java

Barbero Dormilón

public void siguiente(){BLock.lock();try{

System.out.println("Barbero libre");Blibre = true;cBlibre.signal();while (!SillaOcupada)

try{cSillaOcupada.await();}catch (InterruptedException e){}

}finally {BLock.unlock();

}}

Page 57: Programación Concurrente  en Java

Barbero Dormilón

public void finPelar(){BLock.lock();try{

PAbierta = true;cPuertaAbierta.signal();while (PAbierta)

try{cSiguiente.await();}catch (InterruptedException e){};

}finally {BLock.unlock();

}}

Page 58: Programación Concurrente  en Java

Barbero Dormilónpublic void qPelar(int i){

BLock.lock();try{while (!Blibre) try{cBlibre.await();}catch (InterruptedException e){};Blibre = false;SillaOcupada = true;System.out.println("Cliente "+i+" se sienta en la silla");cSillaOcupada.signal();while (!PAbierta) try{cPuertaAbierta.await();}catch (InterruptedException e){};System.out.println("Cliente "+i+" se va");PAbierta = false;cSiguiente.signal();}finally {BLock.unlock();

}}

Page 59: Programación Concurrente  en Java

Barbero Dormilónpublic void qPelar(int i){

BLock.lock();try{

while (!Blibre) try{cBlibre.await();}

catch (InterruptedException e){};Blibre = false;SillaOcupada = true;System.out.println("Cliente "+i+" se sienta en la silla");cSillaOcupada.signal();while (!PAbierta)

try{cPuertaAbierta.await();}catch (InterruptedException e){};System.out.println("Cliente "+i+" se va");PAbierta = false;cSiguiente.signal();

}finally {BLock.unlock();

}}}

Page 60: Programación Concurrente  en Java

Barbero 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 clienteb.finPelar();

}}

}

Page 61: Programación Concurrente  en Java

Barbero 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);

}

}

Page 62: Programación Concurrente  en Java

Barbero Dormilónpublic class UsaBarberia {

public static void main(String[] args){final int N = 125;Barberia b = new Barberia();Barbero bar = new Barbero(b);cliente[] c = new cliente[N];

for (int i = 0; i<N; i++)c[i] = new cliente(b,i);

Thread bh = new Thread(bar);bh.start();Thread[] ch = new Thread[N];

for (int i = 0; i<N; i++)ch[i]= new Thread(c[i]);

for (int i = 0; i<N; i++)ch[i].start();

}}