TEMA 5: Control de la Concurrencia en Java (API Estándar)

40
1 CONTENIDO Exclusión Mutua con código synchronized . Exclusión Mutua con métodos synchronized . Protocolos de Control de la Exclusión Mutua. Interbloqueos Sincronización: wait(), notify(),notifyAll() Condiciones de Guarda Protocolos de Sincronización Diseño de Monitores en Java BIBLIOGRAFÍA RECOMENDADA: [Göe06] Göetz et al. Java Concurrency in Practice, 2006. [Oaks04] Oaks & Wong. Java Threads. O`Reilly, 2004. [Well04] Wellings, A, Concurrent and Real Time Programming in Java. John Wiley&Sons ,2004 TEMA 5: Control de la Concurrencia en Java (API Estándar)

Transcript of TEMA 5: Control de la Concurrencia en Java (API Estándar)

Page 1: TEMA 5: Control de la Concurrencia en Java (API Estándar)

1

CONTENIDOExclusión Mutua con código synchronized. Exclusión Mutua con métodos synchronized. Protocolos de Control de la Exclusión Mutua.InterbloqueosSincronización: wait(), notify(),notifyAll()Condiciones de GuardaProtocolos de SincronizaciónDiseño de Monitores en JavaBIBLIOGRAFÍA RECOMENDADA:[Göe06] Göetz et al. Java Concurrency in Practice, 2006.[Oaks04] Oaks & Wong. Java Threads. O`Reilly, 2004.[Well04] Wellings, A, Concurrent and Real Time

Programming in Java. John Wiley&Sons ,2004

TEMA 5: Control de la Concurrencia en Java (API Estándar)

Page 2: TEMA 5: Control de la Concurrencia en Java (API Estándar)

2

EXCLUSIÓN MUTUA ENTRE HILOS§ En Java la exclusión mutua se logra a nivel de

objetos. Todo objeto tiene asociado un cerrojo (lock).

§ Técnicas de Control de la E.M. en Java.§ Bloques sincronizados. El segmento de código que deba

ejecutarse en e.m. se etiqueta como synchronized. Es el equivalente a una región crítica en java.

§ Métodos sincronizados. Aquellos instancias de recursos que deban manejarse bajo e.m. se encapsulan en una clase y todos sus métodos modificadores son etiquetados con la palabra reservada synchronized.

§ Por tanto, dos o más hilos están en e.m. cuando llaman a métodos synchronized de un objeto (de exclusión mutua) o ejecutan métodos que tienen bloques de código sincronizados

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 3: TEMA 5: Control de la Concurrencia en Java (API Estándar)

3

OBJETO DEEX. MUTUA

HILOS EN ESPERA(BLOQUEADOS)

HILO CON BLOQUEO ADQUIRIDO

OBJETO DEEX. MUTUA

HILOS EN ESPERA(BLOQUEADOS)

HILO LIBERANDO BLOQUEO

HILO CON BLOQUEO ADQUIRIDO

CUESTIONES A DETERMINAR:• ¿Cómo establecer el bloqueo a nivel sintáctico?• ¿Cuál es la semántica del bloqueo?• ¿Qué política de acceso se da a los hilos en espera?

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 4: TEMA 5: Control de la Concurrencia en Java (API Estándar)

4

public metodo_x (parametros){//resto codigo del metodo; no se ejecuta en em.

/*comienzo sección crítica*/ synchronized (object){ Bloque de sentencias críticas //Todo el código situado entre llaves se ejecuta bajo e.m.

}/*fin sección crítica*/

}//metodo_x

Sintaxis para Bloques de Código Sincronizado

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 5: TEMA 5: Control de la Concurrencia en Java (API Estándar)

5

n Son una implementación del concepto de región crítica.

n Un bloque de código está sincronizado respecto de un objeto (puede ser el mismo objeto, this)

n El bloque sólo se ejecuta si se obtiene el cerrojo sobre el objeto.

n Útil para adecuar código secuencial a un entorno concurrente

Semántica de Bloques de Código Sincronizado

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 6: TEMA 5: Control de la Concurrencia en Java (API Estándar)

6

Política de Acceso de los Hilos en Espera

Datos en em.

ESPERA POR CERROJO

CERROJO

CódigoSincronizado

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 7: TEMA 5: Control de la Concurrencia en Java (API Estándar)

7

public class Ejemplo_Bloques_Sincronizados {private int Valor_1;private int Valor_2;protected Object Cerrojo_1 = new Object ();protected Object Cerrojo_2 = new Object ();

public int Observa_1 () { synchronized (Cerrojo_1){return (Valor_1); } } public void Modifica_1 (int dato) {synchronized (Cerrojo_1){ Valor_1 = dato; } } public void Modifica_2 (int dato) { synchronized (Cerrojo_2){ Valor_2 = dato; } } public int Observa_2 () { synchronized (Cerrojo_2){ return (Valor_2); } } public Ejemplo_Bloques_Sincronizados() { synchronized (Cerrojo_1){ synchronized (Cerrojo_2){ Valor_1 = Valor_2 = 1; } } }}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 8: TEMA 5: Control de la Concurrencia en Java (API Estándar)

8

public class codBloqueo //usa el cerrojo del propio objeto{ private int numVueltas; public codBloqueo(int vueltas){numVueltas = vueltas;}

public void metodo() { synchronized(this) //bloqueo para acceso a seccion critica { for(int i=1; i<=numVueltas; i++) System.out.print(i+" "); } }

}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Acotando la Sección Crítica…

Page 9: TEMA 5: Control de la Concurrencia en Java (API Estándar)

9

public class UsacodBloqueo extends Thread{

codBloqueo cerrojo; //referencia a objeto compartido

public UsacodBloqueo(codBloqueo l) {cerrojo = l;} public void run() { cerrojo.metodo(); //llamada a metodo que tiene codigo sincronizado }

public static void main(String[] args) { codBloqueo aux = new codBloqueo(200); UsacodBloqueo h1 = new UsacodBloqueo(aux); UsacodBloqueo h2 = new UsacodBloqueo(aux); h2.start(); h1.start(); }}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

… y usándola desde hilos en modo seguro

Page 10: TEMA 5: Control de la Concurrencia en Java (API Estándar)

10

Output sin/con synchronized

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 11: TEMA 5: Control de la Concurrencia en Java (API Estándar)

11

public class MuestraBloqueo implements Runnable { private Object o; public MuestraBloqueo(Object p) //cerrojo como parámetro del constructor {o = p;} public void run() { synchronized(o)//primer hilo que llega adquiere cerrojo { for(int i=1;i<100;i++){ //no habra entrelazado de impresiones System.out.println("Iteracion "+i+" del hilo "+this.toString()); for(int j=1;j<100;j++); //retraso para visualizar } } } public static void main(String[] args) { Object lock = new Object(); //objeto para cerrojo compartido por los hilos Thread h1 = new Thread(new MuestraBloqueo(lock), "hilo 1"); Thread h2 = new Thread(new MuestraBloqueo(lock), "hilo 2"); h1.setPriority(Thread.MIN_PRIORITY); h2.setPriority(Thread.MAX_PRIORITY); h1.start(); h2.start(); }}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Pasando el cerrojo como parámetro

Page 12: TEMA 5: Control de la Concurrencia en Java (API Estándar)

12

n Descargue otra vez nuestra vieja clase Cuenta_Banca.java

n Decida que código debe ser sincronizado, y sincronícelo. La nueva clase será Cuenta_Banca_Sync.java

n Escriba un programa multihebrado que use objetos de la clase anterior.Llámelo UsaCuenta_Banca_Sync.java

EJERCICIOS

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 13: TEMA 5: Control de la Concurrencia en Java (API Estándar)

13

Sintaxis para Métodos Sincronizados

public synchronized metodo_x (parametros){ //Bloque de sentencias del método //Todo el código del método se ejecuta en

em.}//metodo_x

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 14: TEMA 5: Control de la Concurrencia en Java (API Estándar)

14

Semántica para Métodos Sincronizados

n Un método synchronized fuerza la e.m entre todos los hilos que lo invocan

n También fuerza la e.m. con otros métodos synchronized del objeto

n Cualquier hilo que trate de ejecutar un método sincronizado deberá esperar si otro método sincronizado ya está en ejecución

n Los métodos no sincronizados pueden seguir ejecutándose concurrentemente

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 15: TEMA 5: Control de la Concurrencia en Java (API Estándar)

15

n El cerrojo está asociado a cada instancia de una clase (objeto)

n Los métodos de clase (static) también pueden ser synchronized.

n En clases heredadas, los métodos sobreescritos pueden ser sincronizados o no, sin afectar a cómo era (y es) el método de la superclase.

Además…

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 16: TEMA 5: Control de la Concurrencia en Java (API Estándar)

16

public class MuestraBloqueoObjeto {

public synchronized void metodoA() { for(int i=1;i<100;i++){ System.out.println("Iteracion "+i+" del metodo A "); for(int j=1;j<100;j++);//retraso para visualizar } System.out.println("metodo A liberando cerrojo..."); }

public synchronized void metodoB() { for(int i=1;i<100;i++){ System.out.println("Iteracion "+i+" del metodo B "); for(int j=1;j<100;j++); } System.out.println("metodo B liberando cerrojo..."); }

}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Control con Métodos Sincronizados

Page 17: TEMA 5: Control de la Concurrencia en Java (API Estándar)

17

public class UsaMuestraBloqueoObjeto implements Runnable{

private MuestraBloqueoObjeto p; //referencia objeto compartidoprivate int caso;

public UsaMuestraBloqueoObjeto(MuestraBloqueoObjeto o, int val) {p=o; caso=val;}

public void run() { switch(caso){ case 0: p.metodoA(); break; case 1: p.metodoB(); break; } }

public static void main(String[] args) { MuestraBloqueoObjeto monitor = new MuestraBloqueoObjeto(); Thread h1 = new Thread(new UsaMuestraBloqueoObjeto(monitor,0)); Thread h2 = new Thread(new UsaMuestraBloqueoObjeto(monitor,1)); h1.start(); h2.start(); }}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 18: TEMA 5: Control de la Concurrencia en Java (API Estándar)

18

/*Ejemplo de Exclusion Mutua entre Hilos *@author Antonio J. Tomeu *Varios hilos concurrentes modifican contador protegido bajo e.m. *El metodo que incrementa al contador es synchronized *Crea un array de hilos que incrementan bajo e.m. el contador *Sintaxis de uso: java ExMutua n m donde: *n es el numero de hilos concurrentes *m es el valor inicial del contador*/

class ObCritico{ private int Dato; //Contiene el objeto critico

public ObCritico (int VInicial) //el constructor {Dato = VInicial;}

public synchronized void Incremento() //e ejecutar bajo e.m. {Dato++;}

public int Valor () //hace una lectura. No necesita e.m. {return (Dato);}}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 19: TEMA 5: Control de la Concurrencia en Java (API Estándar)

19

public class ExMutua extends Thread{ private ObCritico SC; public ExMutua (ObCritico SecCritica) {SC = SecCritica;} public void run() { for(;;)

{ SC.Incremento(); System.out.println("El hilo con id. "+getName()+" ha ajustado el objeto a valor "+SC.Valor());}

}public static void main(String[] args){ if (args.length !=2) { System.err.println ("Sintaxis: java ExMutua n m"); System.exit (1); }int NumHilos = Integer.valueOf(args[0]).intValue(); //num. de hilosObCritico ContadorCritico = new ObCritico

(Integer.valueOf(args[1]).intValue());

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 20: TEMA 5: Control de la Concurrencia en Java (API Estándar)

20

ExMutua [] Hilos = new ExMutua [NumHilos];

for(int i=0; i<=NumHilos-1; i++)

{Hilos[i] = new ExMutua (ContadorCritico);}

for(int i=0; i<=NumHilos-1; i++)

{Hilos[i].start();}

}

}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 21: TEMA 5: Control de la Concurrencia en Java (API Estándar)

21

n Descargue otra vez nuestra vieja clase Cuenta_Banca.java

n Decida que métodos deben ser sincronizados, y sincronícelos. La nueva clase será Cuenta_Banca_Sync_Met.java

n Escriba un programa multihebrado que use objetos de la clase anterior.Llámelo Usa_Cuenta_Banca_Sync_Met.java

EJERCICIOS

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 22: TEMA 5: Control de la Concurrencia en Java (API Estándar)

22

n Acceder al recurso compartido donde sea necesario

n Definir un objeto para control de la exclusión mutua (o usar el propio objeto, this).

n Sincronizar el código crítico utilizando el cerrojo del objeto de control dentro de un bloque synchronized de instrucciones

Protocolo 1 de Control de E.M. en Java

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 23: TEMA 5: Control de la Concurrencia en Java (API Estándar)

23

n Encapsular el recurso crítico en una clase.n Definir todos los métodos de la clase como

no estáticos y synchronizedn O al menos, todos los modificadoresn Crear hilos que compartan una instancia

de la clase creadan !OJO¡ Esto no provee sincronización

Protocolo 2 de Control de E.M. en Java

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 24: TEMA 5: Control de la Concurrencia en Java (API Estándar)

24

EJERCICIOS

n Modele con una clase alguna situación donde haya presencia de concurrencia y condiciones de concurso. Guárdela en Recurso.java

n Modifique la clase anterior protegiendo al recurso mediante el Protocolo 2 de Control de la E.M. Llame a la clase Recurso_em.java.

n Escriba ahora un programa llamado Prueba_Recurso_em.java que haga uso de la clase anterior. Debe crear varios hilos concurrentes mediante implementación de la interfaz Runnable.

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 25: TEMA 5: Control de la Concurrencia en Java (API Estándar)

25

n Un método sincronizado puede invocar a otro método sincronizado sobre el mismo objeto (REENTRANCIA)

n Permite llamadas recursivas a métodos sincronizados

n Permite invocar método herededados sincronizados

n Descargue obj_protected.java y usa_bj_protected.java. Pruébelos: ¿a qué conclusión llega?

n Incorpore ahora un método m3() recursivo dentro de obj_protected. java. ¿Se mantiene su conclusión?

La Posesión del Bloqueo es por Hilo…

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 26: TEMA 5: Control de la Concurrencia en Java (API Estándar)

26

n Se producen cuando hay condiciones de espera de liberación de bloqueos cruzadas entre dos hilos

n Sólo pueden evitarse mediante un análisis cuidadoso y riguroso del uso de código sincronizado

INTERBLOQUEO (DEADLOCK) ENTRE HILOS (1/2)

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 27: TEMA 5: Control de la Concurrencia en Java (API Estándar)

27

public class Deadlock {public static void main(String[] args) { final Object region_A = new Object(); final Object region_B = new Object();

Thread Hilo_A = new Thread(new Runnable(){public void run()

{ synchronized(region_A) { synchronized(region_B) { calculo();

} } } });

Hilo_B.start(); Hilo_A.start(); }}

INTERBLOQUEO (DEADLOCK) ENTRE HILOS (2/2)PATRÓN ESTÁNDAR

Thread Hilo_B = new Thread(new Runnable(){public void run()

{ synchronized(region_B) { synchronized(region_A) { calculo(); } } } });

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 28: TEMA 5: Control de la Concurrencia en Java (API Estándar)

28

SINCRONIZACIÓN ENTRE HILOS§ En Java la sincronización entre hilos se logra con los métodos wait,

notify y notifyAll (Clase Object)

§ Deben ser utilizados únicamente dentro de métodos o código de tipo synchronized.

§ Cuando un hilo llama a un método que hace wait las siguientes acciones son ejecutadas atómicamente:1. Hilo llamante suspendido y bloqueado.2. Exclusión mutua sobre el objeto liberada.3. Hilo colocado en una cola única (el wait-set) de espera asociada al objeto.

§ Cuando un hilo llama a un método que hace notify, uno de los hilos bloqueados en la cola pasa a listo. Java no especifica cuál. Depende de la implementación (JVM)Si se llama al método notifyAll todos los hilos de dicha cola son desbloqueados y pasan a listos. Accederá al objeto aquél que sea planificado.

§ Si el wait-set está vacío, notify y notifyAll no tienen efecto.

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 29: TEMA 5: Control de la Concurrencia en Java (API Estándar)

29

EL CONJUNTO DE ESPERA (wait-set)

wait-setwait-set

Conjunto de espera asociado al cerrojo e.m del objetoConjunto de espera asociado al cerrojo e.m del objeto

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 30: TEMA 5: Control de la Concurrencia en Java (API Estándar)

30

public class aDormir extends Thread{ public aDormir() {} public void run() { System.out.println("El hilo "+this.getName()+" dijo: mi vida activa

fue breve..."); synchronized(this) { try{wait();}catch (InterruptedException e){} //cada hilo dormido sobre

su propio cerrojo System.out.println(this.getName()+" dijo: pero he revivido..."); } }

public void despertar(){synchronized (this){notify();}}

public static void main(String[] args) { aDormir [] h = new aDormir[10]; for(int i=0; i<10;i++) {h[i]=new aDormir(); h[i].start();} h[5].despertarUno();

}}

Page 31: TEMA 5: Control de la Concurrencia en Java (API Estándar)

31

public class aDormir2 extends Thread{

Object lock; public aDormir2(Object l) {lock=l;} public void run() { System.out.println("El hilo "+this.getName()+" dijo: mi vida activa fue

breve..."); synchronized(lock) { try{lock.wait();}catch (InterruptedException e){} //hilos dormidos sobre

un cerrojo común System.out.println(this.getName()+" dijo: pero he revivido..."); } }

public void despertar(){synchronized (lock){lock.notify();}} public void despertarTodos(){synchronized (lock){lock.notifyAll();}}

public static void main(String[] args) { Object cerrojo = new Object(); aDormir2 [] h = new aDormir2[10]; for(int i=0; i<10;i++) {h[i]=new aDormir2(cerrojo); h[i].start();} h[5].despertar();

h[5].despertarTodos();System.out.print("Todos terminaron..."); //¿observa algo anómalo?

}}

PRUEBE AHORA aDormir3.java

Page 32: TEMA 5: Control de la Concurrencia en Java (API Estándar)

32

§ No se puede escribir código de hilos asumiendo que un hilo concreto recibirá la notificación.

§ Diferentes hilos pueden estar bloqueados sobre un mismo objeto a la espera de diferentes condiciones

§ Dado que notifyAll() los despierta a todos de forma incondicional, es posible que reactive hilo para los cuales no se cumple aún la condición de espera.

§ Solución: siempre que el hilo usuario invoca a wait(), lo primero al despertar es volver a comprobar su condición particular, volviendo al bloqueo si ésta aún no se cumple.

Condiciones de Guarda

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 33: TEMA 5: Control de la Concurrencia en Java (API Estándar)

33

Patrón de Código para Condiciones de Guarda

public synchronized void m_receptor_señal(){//hilo usuario se bloqueará en el metodo a espera de

condición//el hilo usuario pasa al wait-setwhile (!condicion) try{wait();} //condición de guarda catch ()(Exception e){}//codigo accedido en e.m.}

------------------------------------------------------------

public synchronized void m_emisor_señal(){//hilo usuario enviará una señal a todos los hilos del wait-

set//la guarda garantiza que se despiertan los adecuados

//codigo en e.m. que cambia las condiciones de estadonotiyAll();}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 34: TEMA 5: Control de la Concurrencia en Java (API Estándar)

34

// Thread A public void waitForMessage() {

while (hasMessage == false) { Thread.sleep(100);

}}

// Thread B public void setMessage(String message) { ...hasMessage = true;

Protocolo de Sicronización Inter-Hilos en JavaCon Espera Ocupada

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 35: TEMA 5: Control de la Concurrencia en Java (API Estándar)

35

// Thread A public synchronized void waitForMessage() {try {

wait(); } catch (InterruptedException ex) { } }

// Thread B public synchronized void setMessage(String message) { ...notify();

OJO: No es perfecto. La señal puede perderse (y se pierde) si nadie está esperándola.

SOLUCIÓN: Utilizar condiciones. A sólo se bloqueará si al comprobar una condición ésta es falsa, y B se asegurará de hacer que la condición sea verdadera antes de notificar.

Protocolo de Sicronización Inter-Hilos en Javacon sincronización wait-notify

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 36: TEMA 5: Control de la Concurrencia en Java (API Estándar)

36

class Sincro

{ int Turno;

public Sincro(int t){Turno = t;}

public synchronized void metodo1(){

while(Turno != 1) try {wait();} catch (Exception e){}System.out.println(" Turno al hilo1..."); Turno=2;notifyAll();

}

public synchronized void metodo2(){

while(Turno != 2) try {wait();} catch (Exception e){}System.out.println("Turno al hilo 2..."); Turno = 3;notifyAll();

}

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Patrón “sincronizador”

Page 37: TEMA 5: Control de la Concurrencia en Java (API Estándar)

37

public synchronized void metodo3(){

while(Turno != 3) try {wait();} catch (Exception e){}System.out.println("Turno al hilo 3..."); Turno = 1;notifyAll();

}

}//Sincro

class Hilo1 extends Thread{

Sincro ref;public Hilo1(Sincro obj){ref=obj;}public void run(){

for(;;){ref.metodo1();}}

}//Hilo1

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 38: TEMA 5: Control de la Concurrencia en Java (API Estándar)

38

class Hilo2 extends Thread{

Sincro ref;public Hilo2(Sincro obj){ref=obj;}public void run(){

for(;;){ref.metodo2();}}

}//Hilo2

class Hilo3 extends Thread{

Sincro ref;public Hilo3(Sincro obj){ref=obj;}public void run(){

for(;;){ref.metodo3();}}

}//Hilo3

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 39: TEMA 5: Control de la Concurrencia en Java (API Estándar)

39

public class Sincronizacion{

public static void main (String [] args){

Sincro m = new Sincro(2);new Hilo1(m).start();new Hilo2(m).start();new Hilo3(m).start();System.out.println("hilos lanzados...");

}//main}//Sincronizacion

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar

Page 40: TEMA 5: Control de la Concurrencia en Java (API Estándar)

40

n Utilizando condiciones de guarda, modele un protocolo de sincronización simple Hilo B -> Hilo A que sea completamente correcto.

n Guarde su código en ficheros S1.java, S2.java…

EJERCICIO

© Antonio Tomeu Control de la Concurrencia en Java: API Estándar