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)
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
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
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
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
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
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
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…
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
10
Output sin/con synchronized
© Antonio Tomeu 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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();
}}
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
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
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
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
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
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”
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
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
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
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