Hebras y monitores Departamento de Sistemas Informáticos y Programación Universidad Complutense de Madrid 21 de marzo de 2006
Threads Extendiendo la clase java.lang.thread. public class PrThread extends Thread{ public PrThread( String s) { super(s); public final void run() { boolean sigue=true; for (int i=0; i<100 && sigue; i++) { try { System.out. println( getname()+":"+i); sleep(20); catch ( InterruptedException e) { System.out. println( getname()+" interrumpida"); sigue=false; public static final void main( final String[] args){ Thread p = new PrThread("mia"); p.start(); 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
Threads Implementado el interfaz java.lang.runnable. public class PrRunnable implements Runnable { public final void run() { Thread hebra = Thread. currentthread(); boolean sigue=true; for (int i=0; i<100 && sigue; i++) { try { System.out. println( hebra. getname()+":"+i); hebra. sleep(20); catch ( InterruptedException e) { System.out. println( hebra. getname()+" interrumpida"); sigue=false; public static final void main(final String[] args) { Thread p = new Thread(new PrRunnable(),"mia"); p.start(); 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
Ciclo de vida de una hebra Tras crear una hebra y se ejecuta el método start, la hebra puede estár: 1 En ejecución. 2 Suspendida: ha ejecutado sleep, join, wait.
Parar una hebra Usar el método interrupt public static void ex1() throws InterruptedException{ Thread h = new PrThread("1"); h.start(); Thread. sleep(100); h.interrupt(); System.out. println(h. isinterrupted()); 1 2 3 4 5 6 7 Métodos Deprecated: stop, suspend, resume. Una hebra para cunado está Not Runnnable, ha ejecutado sleep, join, wait. Pueden lanzar InterruptedException, la hebra decide si para o sigue.
Esperamos a que una hebra acabe Método: join public static void ex2() throws InterruptedException { Thread h1 = new PrThread("1"); Thread h2 = new PrThread("2"); Thread h3 = new PrThread("3"); h1.start(); h2.start(); h3.start(); h1.join(); h2.join(); h3.join(); 1 2 3 4 5 6 7 8 9 10 11
Esperamos a que una hebra acabe Método: join h3.start() h1.start() h2.start() h1.join() h2.join() h3.join()
Esperamos a que una hebra acabe Método: join h1.start() h2.start() 1 h1.start(): h1 se ejecuta. h3.start() h1.join() h2.join() h3.join()
Esperamos a que una hebra acabe Método: join h3.start() h1.start() h2.start() 1 h1.start(): h1 se ejecuta. 2 h2.start(): h2 se ejecuta. h1.join() h2.join() h3.join()
Esperamos a que una hebra acabe Método: join h3.start() h1.start() h2.start() 1 h1.start(): h1 se ejecuta. 2 h2.start(): h2 se ejecuta. 3 h3.start(): h3 se ejecuta. h1.join() h2.join() h3.join()
Esperamos a que una hebra acabe Método: join h1.start() h2.start() h3.start() h1.join() 1 h1.start(): h1 se ejecuta. 2 h2.start(): h2 se ejecuta. 3 h3.start(): h3 se ejecuta. 4 h1.join(): esperamos a que h1 pare. h2.join() h3.join()
Esperamos a que una hebra acabe Método: join h1.start() h2.start() h3.start() h1.join() h2.join() 1 h1.start(): h1 se ejecuta. 2 h2.start(): h2 se ejecuta. 3 h3.start(): h3 se ejecuta. 4 h1.join(): esperamos a que h1 pare. 5 h2.join(): esperamos a que h2 pare. h3.join()
Esperamos a que una hebra acabe Método: join h1.start() h2.start() h3.start() h1.join() h2.join() h3.join() 1 h1.start(): h1 se ejecuta. 2 h2.start(): h2 se ejecuta. 3 h3.start(): h3 se ejecuta. 4 h1.join(): esperamos a que h1 pare. 5 h2.join(): esperamos a que h2 pare. 6 h3.join(): esperamos a que h3 pare (no hace falta).
Cerrojos de objetos Cada objeto en Java tiene un cerrojo synchronized (obj) { /* */ 1 2 3 El cerrojo puede abarcar a todo un método type method (...) { synchronized(this) { /* */ 1 2 3 4 synchronized type method (...) { /* */ 1 2 3
Variables condición Qué ocurre si todos los métodos son synchronized?
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez Qué falta para tener un monitor?
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez Qué falta para tener un monitor? las variables condición.
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez Qué falta para tener un monitor? las variables condición. Todos los objetos tienen los métodos wait(), notify() y notifyall().
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez Qué falta para tener un monitor? las variables condición. Todos los objetos tienen los métodos wait(), notify() y notifyall(). Un objeto con todos los métodos synchronized es un monitor con una sola variable condición.
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez Qué falta para tener un monitor? las variables condición. Todos los objetos tienen los métodos wait(), notify() y notifyall(). Un objeto con todos los métodos synchronized es un monitor con una sola variable condición. Qué hacemos si hay varias variables condición?
Variables condición Qué ocurre si todos los métodos son synchronized? sólo se puede ejecutar un método a la vez Qué falta para tener un monitor? las variables condición. Todos los objetos tienen los métodos wait(), notify() y notifyall(). Un objeto con todos los métodos synchronized es un monitor con una sola variable condición. Qué hacemos si hay varias variables condición? Todo monitor se puede hacer con una única variable condición, con pérdida de eficiencia: método notifyall().
Monitor puente Tiene dos variables condición: norte y sur. procedure permiso( sentido s) { if (s. equals( Sentido.sur)) { esps++; while ( numn > 0 cups>=maxcupo ) { wait(sur); esps--; nums++; cupn=0; if (espn>0) { cups ++; else { // el sentido norte [[similar]] procedure finpermiso( sentido s) { if (s. equals( Sentido.sur)) { nums--; singal( norte); else {//sentido norte [[similar...]] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Puente, versión Java fácil private int espn = 0, esps = 0; private int numn = 0, nums = 0; private int cupn = 0, cups = 0; private int maxcupo; public synchronized void permiso( Sentido s) throws InterruptedException{ if ( s.equals(sentido.sur) ) { esps ++; while ( numn > 0 cups >=maxcupo ) { wait(); esps --; nums++; if ( espn>0 ) { cups++; cupn=0; else { /* sentido Norte, complementario al anterior */ public synchronized void finpermiso( Sentido s) { if ( s.equals(sentido.sur) ) { nums--; else { numn--; notifyall(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Implementación de monitores 1 a idea: cada objeto es un semáforo Object norte, sur; public synchronized void permiso( Sentido s) { if ( s.equals(sentido.sur) ) { esps ++; while ( numn>0 cups>=maxcupo ) { sur.wait(); /*... */ else { espn ++; while ( nums>0 cupn>=maxcupo ) { norte.wait(); /*... */ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Si el proceso se queda esperando en sur.wait(), no libera el objeto monitor: Se queda bloqueado!!
Implementación de monitores 1 Cada objeto es un semáforo.
Implementación de monitores 1 Cada objeto es un semáforo. 2 Por cada variable condición es necesario un objeto semáforo.
Implementación de monitores 1 Cada objeto es un semáforo. 2 Por cada variable condición es necesario un objeto semáforo. 3 Antes de esperar en un wait hemos de abandonar el monitor.
Implementación de monitores 1 Cada objeto es un semáforo. 2 Por cada variable condición es necesario un objeto semáforo. 3 Antes de esperar en un wait hemos de abandonar el monitor. 4 Es necesario un semáforo para entrar y salir del monitor.
Semáforos public final class Semaforo { String nombre; int s; public Semaforo(int inicial, String _nombre) { s=inicial; nombre=_nombre; // Semaforo constructor public synchronized void P() throws InterruptedException { while (s==0) { wait(); s--; public synchronized void V() { s++; notify(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Variables Condición I 1 Una variable condición básicamente es un semáforo. 2 Necesitamos también el semáforo del monitor para poder salir antes de un wait y entrár después. Semaforo mutex; public final void Wait() throws InterruptedException { mutex.v(); numespera ++; s.p(); mutex.p(); 1 2 3 4 5 6 7 Nota: el método wait es final en la clase Object.
Variables Condición II public class VariableCondicion { int numespera; Semaforo s; Semaforo mutex; public VariableCondicion( Semaforo _mutex, String nombre) { s = new Semaforo(0,nombre); numespera = 0; mutex = _mutex; public synchronized final void Signal() { if (numespera>0) { numespera--; s.v(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Puente I public class Puente { VariableCondicion norte,sur; Semaforo mutex; private int espn = 0, esps = 0; private int numn = 0, nums = 0; private int cupn = 0, cups = 0; private int maxcupo; public void permiso( Sentido s) { mutex.p(); if ( s.equals(sentido.sur) ) { esps ++; while ( numn > 0 cups >=maxcupo ) { sur.wait(); esps --; nums++; if ( espn>0 ) { cups++; cupn=0; else { /* sentido Norte, caso simetrico al anterior */ mutex.v(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Puente II public void finpermiso( Sentido s) { mutex.p(); if ( s.equals(sentido.sur) ) { nums--; if (nums==0) {norte.signal();; else { numn--; if (numn==0) {sur.signal();; mutex.v(); public Puente(int cupo) { maxcupo = cupo; mutex = new Semaforo(1," mutex"); norte = new VariableCondicion(mutex," norte"); sur = new VariableCondicion(mutex," norte"); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Puente III package simulacion. puente; public class Coche extends Thread { private Sentido sentido; private int id, tenpuente; private long horainicio; private Puente puente; public Coche (int i, Sentido s, int t, Puente p){ sentido = s; id = i; tenpuente = t; puente = p; public void run() { System.out. println("el coche "+id+" quiere pasar en sentido "+ sentido + ":" + SimulaPuente. tiemposimulacion()); puente. permiso( sentido); System.out. println("el coche "+id+" áest pasando en sentido "+ sentido + ":" + SimulaPuente. tiemposimulacion()); try { this. sleep( tenpuente*1000); catch ( InterruptedException e ) { puente. finpermiso( sentido); System.out. println("el coche "+id+" ha pasado en sentido "+ sentido + ":" + SimulaPuente. tiemposimulacion()); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Puente IV public SimulaPuente( double tllegadan, double tllegadas, int tpuente, int tmax) 1{ Puente puente = new Puente(5); 2 GeneradorCoches generanorte = new GeneradorCoches( Sentido.norte, 3 tllegadan, 4 tpuente, tmax, 5 3333, 6 puente); 7 GeneradorCoches generasur = new GeneradorCoches( Sentido.sur, 8 tllegadas, 9 tpuente, tmax, 10 1111, 11 puente); 12 generanorte. start(); 13 generasur. start(); 14 horainicio = System. currenttimemillis(); 15 try { 16 generanorte.join(); 17 generasur.join(); 18 catch ( InterruptedException e ) { 19 // end of try-catch 20 21
Puente V package simulacion. puente; 1 class SimulaPuente { 2 private static int numusuario = 0; 3 private static long horainicio; 4 5 public synchronized static int sigusuario() { 6 numusuario ++; 7 return numusuario; 8 9 public static int tiemposimulacion() { 10 return (int)(( System. currenttimemillis() - horainicio)/( long)1000); 11 12 public SimulaPuente( double tllegadan, double tllegadas, int tpuente, int tmax) 13 { /* Cuerpo de simula Puente */ 14 15 public static void main (String[] args) { 16 SimulaPuente p = new SimulaPuente(2, 2, 1, 20); 17 // end of main () 18 19
Puente VI package simulacion. puente; 1 import soporte. Aleatorio; 2 public class GeneradorCoches extends Thread { 3 private Sentido sentido; 4 private double tllegadamediocoches; 5 private int tenpuente; 6 private int tmaxsimulacion; 7 private Aleatorio aleatorio; 8 private Puente puente; 9 public GeneradorCoches (Sentido stdo, double tll, int tp, int tm, int sm, Puente 10 p){ sentido = stdo; 11 tllegadamediocoches = tll; 12 tenpuente = tp; 13 tmaxsimulacion = tm; 14 aleatorio = new Aleatorio(sm); 15 puente = p; 16 17
Puente VII public void run() { 1 System.out. println(" Empezando el sentido "+ sentido); 2 do {// do-while ( tiempo < tmaxsimulacion ) 3 int sigcoche = aleatorio. poisson( tllegadamediocoches); 4 try { 5 this. sleep( sigcoche*1000); 6 7 catch ( InterruptedException e ) { 8 9 // end of try-catch 10 Coche coche = new Coche( SimulaPuente. sigusuario(), sentido, tenpuente, 11 p coche.start(); 12 while ( SimulaPuente. tiemposimulacion() < tmaxsimulacion ); 13 14 15 16
Capturando InterruptedException I Semaforo public synchronized void P() throws InterruptedException{ while (s==0) { try { wait(); /* Si me interrumpen íaqu ocupamos el ásemforo, pero lazamos la óexcepcin */ catch ( InterruptedException e) { s--; throw e; s--; 1 2 3 4 5 6 7 8 9 10 11 12
Capturando InterruptedException II VariableCondicion public final void Wait() throws InterruptedException { 1 mutex.v(); 2 numespera ++; 3 try { 4 s.p(); 5 catch ( InterruptedException e) { 6 /* Se ha interrumpido un proceso esperando en esta ariable ócondicin. 7 * El ásemforo se queda sin ocupar (ver Semaforo.java), áhabr que decir 8que * esta hebra no áest esperando */ 9 numespera--; 10 throw e; 11 12 try { 13 mutex.p(); 14 catch ( InterruptedException e) { 15 /* Se ha interrumpido un proceso que íhaba sido autorizado para entrar 16 * en la variable ócondicin. áhabr que liberar mutex y el ásemforo.*/ 17 numespera--; 18 s.v(); 19 mutex.v(); 20 throw e; 21 22 23
Capturando InterruptedException III public void permiso( Sentido s) throws InterruptedException { try { mutex.p();/* si interrumpen antes de entrar, no entramos y liberamos el monitor. */ catch ( InterruptedException e) { mutex.v(); throw e; if ( s.equals(sentido.sur) ) { esps ++; while ( numn > 0 cups >=maxcupo ) {sur.wait(); esps --; nums++; if ( espn>0 ) {cups++; cupn=0; else { espn ++; while ( nums > 0 cupn >=maxcupo ) {norte.wait(); espn --; numn++; if ( esps>0 ) {cupn++; cups=0; // end of else mutex.v(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Capturando InterruptedException IV public void finpermiso( Sentido s) throws InterruptedException { try { mutex.p();/* Si me interrumpen íaqu tengo que restaurar liberar el monitor y despertar a quien lo necesite*/ catch ( InterruptedException e) { if ( s.equals(sentido.sur) ) { nums--; if (nums==0) {norte.signal();; else { numn--; if (numn==0) {sur.signal();; mutex.v(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Capturando InterruptedException I Es mejor no interrumpir una hebra dentro del monitor, mejor esperar a que salga. Dentro del monitor tenemos una variable ie que indica si la hebra ha sido interrumpida.
Capturando InterruptedException II Es mejor no interrumpir una hebra dentro del monitor, mejor esperar a que salga. Dentro del monitor tenemos una variable ie que indica si la hebra ha sido interrumpida. mutex.p, Wait, no lanzan una excepciones.
Capturando InterruptedException III Es mejor no interrumpir una hebra dentro del monitor, mejor esperar a que salga. Dentro del monitor tenemos una variable ie que indica si la hebra ha sido interrumpida. mutex.p, Wait, no lanzan una excepciones.devolverán una excepción en caso de haberse producido. Si devuelven una excepción el monitor lanza esa excepción al final.
Capturando InterruptedException IV public void permiso( Sentido s) throws InterruptedException { InterruptedException ie=null; ie=mutex.p(ie); if ( s.equals(sentido.sur) ) { esps ++; while ( numn > 0 cups >=maxcupo ) { ie=sur.wait(ie); esps --; nums++; if ( espn>0 ) { cups++; cupn=0; else {// sentido Norte espn ++; while ( nums > 0 cupn >=maxcupo ) {ie=norte.wait(ie); espn --; numn++; if ( esps>0 ) {cupn++; cups=0; mutex.v(); if (ie!=null) { throw ie; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Capturando InterruptedException V public void finpermiso( Sentido s) throws InterruptedException { InterruptedException ie=null; ie=mutex.p(ie); if ( s.equals(sentido.sur) ) { nums--; if (nums==0) {norte.signal();; else {//Sentido norte numn--; if (numn==0) {sur.signal();; mutex.v(); if (ie!=null) { throw ie; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Capturando InterruptedException VI public final InterruptedException Wait( InterruptedException ie) { synchronized (this) { numespera ++; mutex.v(); ie=s.p(ie); ie=mutex.p(ie); return ie; 1 2 3 4 5 6 7
Capturando InterruptedException VII public synchronized InterruptedException P( InterruptedException ie) { while (s==0) { try { wait(); catch ( InterruptedException e) { ie=e; s--; return ie; 1 2 3 4 5 6 7 8 9 10 11
Capturando InterruptedException VIII public void run() { 1 try { 2 System.out. println("el coche "+id+" quiere pasar en sentido "+ sentido 3+ ":" try { 4 puente. permiso( sentido); 5 System.out. println("el coche "+id+" áest pasando en sentido "+ sentido 6 + sleep( tenpuente*1000); 7 8 catch ( InterruptedException e) { 9 System.out. println("el coche "+id+" ha sido interrumpido"); 10 finally { 11 puente. finpermiso( sentido); 12 13 System.out. println("el coche "+id+" ha pasado en sentido "+ sentido + ":" 14 + catch ( InterruptedException e ) { 15 16 // end of try-catch 17 18
Problemas, mejoras Las entradas y las salidas del monitor se hacen de forma expĺıcita.
Problemas, mejoras Las entradas y las salidas del monitor se hacen de forma expĺıcita. Reflexión en Java.
Problemas, mejoras Las entradas y las salidas del monitor se hacen de forma expĺıcita. Reflexión en Java. Control expĺıcito sobre las colas de procesos, variables condición priorizadas.