Monitores en Java

Descargar como ps, pdf o txt
Descargar como ps, pdf o txt
Está en la página 1de 10

Practica 4. Monitores en Java.

Programacion Concurrente. 3o I.S.


Dpt. Lenguajes y Sistemas Informaticos.
Curso 2002/03

C
odigo y m
etodos sincronizados.

Cada objeto tiene un cerrojo interno asociado. El cerrojo de un objeto solamente puede ser adquirido
por una sola hebra en cada momento. El cualificador synchronized sirve para hacer que un bloque
de codigo o un metodo sea protegido por el cerrojo del objeto. Para ejecutar un bloque o un metodo
sincronizado, las hebras deben adquirir el cerrojo del objeto, debiendo esperar si el cerrojo ha sido ya
adquirido por otra hebra.
Si obj es una referencia a un objeto, la siguiente construcci
on hace que las hebras tengan que
adquirir el cerrojo del objeto para ejecutar el bloque de codigo sincronizado.
synchronized (obj) {
bloque de codigo sincronizado
}
Si todas las secciones crticas estan en el codigo de un u
nico objeto, podremos utilizar this para
referenciar al objeto cuyo cerrojo vamos a utilizar:
synchronized (this) {
bloque de codigo sincronizado
}
Podemos hacer que el cuerpo entero de un metodo sea codigo sincronizado:
tipo metodo ( ... ) {
synchronized (this) {
codigo del metodo sincronizado
}
}
La siguiente construcci
on es equivalente a la anterior:

2
synchronized tipo metodo ( ... ) {
codigo del metodo sincronizado
}
Para construir un monitor en Java, creamos una clase con sus metodos sincronizados. De esta
forma solamente una hebra podra ejecutar en cada momento el metodo del objeto.
El siguiente ejemplo muestra un monitor que implementa un contador:
class Contador {
// monitor contador
private int actual;
public Contador (int inicial) {
actual = inicial;
}
public synchronized void inc () {
actual++;
}
public synchronized void dec () {
actual--;
}
public synchronized int valor () {
return actual;
}
}
class Usuario extends Thread {
// clase hebra usuaria
private Contador cnt;
public Usuario (String nombre, Contador cnt) {
super (nombre);
this.cnt = cnt;
}
public void run () {
for (int i=0; i<1000; i++) {
cnt.inc ();
System.out.println ("Hola, soy " + this.getName() +
", mi contador vale " + cnt.valor());
}
}
}
class EjemploContador {
// principal
final static int nHebras = 20;
public static void main (String[] args) { // metodo principal
Contador cont1 = new Contador (10);
Usuario hebra[] = new Usuario[nHebras];
for (int i=0; i<nHebras; i++) {
hebra[i] = new Usuario ("la hebra-"+i, cont1); // crea hebras
hebra[i].start();
// lanza hebras
}
}
}

M
etodos de espera y notificaci
on.

En Java no existe el concepto de variable condici


on. Podramos decir que cada monitor en Java tiene
una u
nica variable condici
on an
onima. Los monitores de Java implementan la disciplina signal and
continue.
Los metodos wait(), notify() y notifyAll() implementan los mecanismos de espera y notificacion de los monitores Java. Estos metodos solamente pueden ser llamados por una hebra cuando
esta posee el cerrojo del objeto, es decir, desde un bloque o un metodo sincronizado.
La invocacion al metodo wait() provoca que la hebra actual se bloquee y sea colocada en una
cola de espera asociada al objeto monitor. El cerrojo del objeto es liberado, para que otras hebras
puedan ejecutar metodos del objeto. Sin embargo, otros cerrojos poseidos por la hebra suspendida
son retenidos por esta.
La invocacion al metodo notify() provoca que, si hay alguna hebra bloqueada en wait(), se escoja
una cualquiera de forma arbitraria, y se saque de la cola de wait() pasando esta al estado preparado.
La hebra que invoco notify() seguir
a ejecut
andose dentro del monitor. La hebra se
nalada debera

adquirir el cerrojo del objeto para poder ejecutarse. Esto


significara esperar al menos hasta que la
hebra que invoco notify() libere el cerrojo, bien por la ejecuci
on de una llamada a wait(), o bien
por la salida del monitor. La hebra se
nalada no tiene prioridad alguna para ejecutarse en el monitor.
Puede ocurrir que, antes de que la hebra se
nalada pueda volver a ejecutarse, otra hebra adquiera el
cerrojo del monitor.
La invocacion al metodo notifyAll() produce el mismo resultado que una llamada a notify()
por cada hebra bloqueada en la cola de wait(): todas las hebras bloqueadas pasan al estado preparado.

Sincronizaci
on con monitores en Java.

El siguiente monitor tiene como objetivo sincronizar a 4 hebras de acuerdo con unas determinadas
reglas. La salida del programa es una hilera de Ws, Xs, Ys y Zs que cumplen lo siguiente:
El n
umero total de Ws y Xs que hay desde el comienzo (posicion m
as a la izquierda) hasta
cualquier punto de la hilera de salida no puede exceder al n
umero total de Ys y Zs que hay en
la hilera de salida hasta dicho punto.
El n
umero total de Ws que hay desde el comienzo hasta cualquier punto de la hilera de salida
no puede ser superior al doble del n
umero de Xs que hay hasta dicho punto.
Despues de una Y en la hilera de salida han de aparecer 1 o m
as Zs antes de que aparezca otra
nueva Y.
Las siguientes hileras de salida violan alguna de las reglas anteriores:
XWYZY (no cumple la regla 1 en las tres primeras posiciones)
ZWYZZXW (no cumple la segunda regla desde la segunda a la quinta posicion)
ZZYXWWY (no cumple la regla 3, pero la hilera YZZXWWY si es correcta)

4
Los datos de entrada al programa consisten en 5 datos de tipo entero (int): w, x, y, z y e (tiempo
de ejecuci
on del programa). Se supone que dichos datos son suministrados directamente desde la lnea
de ordenes, utilizando la clase GetOpt, de la forma siguiente:
$ java p41 -w <num> -x <num> -y <num> -z <num> -e <num>

class Control{
//** declaracion de las variables del monitor

public synchronized void entra_W(){


//** codigo del metodo
nW++;
}

public synchronized void entra_X(){


//** codigo del metodo
nX++;
}
public synchronized void sale_X(){
//** notificar a los procesos
}
public synchronized void entra_Y(){
//** codigo del metodo

}
public synchronized void sale_Y(){
nY++;
//** modificacion de condicion y notificar a los procesos

}
public synchronized void sale_Z(){
nZ++;
//** modificacion de condicion y notificar a los procesos

}
}
class Pw extends p41 implements Runnable
{

5
public void run(){
Random randGen= new Random();
while (true){
try{
int siesta = 1 + Math.abs((randGen.nextInt())%1000)*w;
Thread.sleep(siesta);
}
catch(InterruptedException ex){
System.err.println("Siesta interrumpida en W"+ex);
}
m.entra_W();
System.out.print("W"); System.out.flush();
m.sale_W();
}
}
}
// Ahora hay que definir las otras clases Px, Py, Pz

/* ***************************************************************************************
*Nota: Para que el programa funcione, falta crear un monitor de la clase Control en algun
*lugar(?), junto con su atributo de ambito adecuado: public, private, static, ... que haga
*posible la ejecucion correctamente sincronizada de las hebras...
*******************************************************************************************/
class p41{
static
static
static
static

int
int
int
int

w=
x=
y=
z=

1;
1;
1;
1;

public static void main(String[] args) {


GetOpt opt= new GetOpt (args, "Uw:x:y:z:E:");
int e= 20;
int opcion= -1;
while((opcion = opt.getopt()) != opt.optEOF) //procesa argumentos
if ((char) opcion == U)
{
System.out.println("Uso: -w <tiempo de siesta del proceso W>");
System.out.println("
-x <tiempo de siesta del proceso X>");
System.out.println("
-y <tiempo de siesta del proceso Y>");
System.out.println("
-z <tiempo de siesta del proceso Z>");
System.out.println("
-E <tiempo de ejecucion>");
System.exit(1);
}

6
else
w=
else
x=
else
y=
else
z=
else
e=
else

if ((char) opcion == w)
opt.processArg(opt.optArgGet(),
if ((char) opcion == x)
opt.processArg(opt.optArgGet(),
if ((char) opcion == y)
opt.processArg(opt.optArgGet(),
if ((char) opcion == z)
opt.processArg(opt.optArgGet(),
if ((char) opcion == E)
opt.processArg(opt.optArgGet(),
System.exit(1);

w);
x);
y);
z);
e);

//** Crear las hebras

//** Lanzar las hebras

try{
Thread.sleep(e*1000);
}
catch(InterruptedException ex){
System.err.println("Siesta interrumpida en Principal"+ex);
}
//** Terminar la ejecucion de las hebras

System.out.println();
System.out.println("Se acabo!!!");
System.exit(0);
}
}

Se pide:
Completar el codigo del monitor Control y del programa.
Compilar y validar los resultados del programa, comprobando que las hileras de salida que se
producen cumplen las reglas enunciadas al principio.
Contestar a las siguientes preguntas:
1. Por que no se ha incluido en el monitor Control un metodo de entrada para la hebra Z.
2. Por que no se ha incluido en el monitor Control un metodo de salida para la hebra W.
3. Justificar el atributo de
ambito que se haya elegido para declarar la instancia de la clase
Control que pide el programa. Por que no funciona el programa declarando otra clase de
atributo.
4. Enviar la contestacion a las preguntas anteriores en un archivo de texto, adem
as del archivo
con el programa que se ha pedido.

El problema del barbero durmiente.

El problema del barbero durmiente es representativo de cierto tipo de problemas reales: ilustra perfectamente la relaci
on de tipo cliente-servidor que a menudo aparece entre los procesos.
Una barbera tiene una puerta, unas cuantas sillas para que esperen los clientes y la silla del
barbero. Debido a que la barbera es peque
na, s
olo un cliente o el barbero pueden realizar movimientos
dentro del local en cada momento. Cuando no hay ning
un cliente en la barbera, el barbero se sienta
en su silla y se duerme. El barbero s
olo puede hacer una cosa a la vez. Aparte de dormir, la actuacion
del barbero consiste en: pelar, esperar a que salga el cliente y le pague, avisar a un cliente sentado en
una silla. Si un cliente encuentra al barbero durmiendo, lo despierta, se sienta en su silla, y espera
a que el barbero termine de pelarlo. Salvo que el barbero este durmiendo, un cliente que entra en
la barbera ha de sentarse en una silla y esperar hasta que se le avise (incluso si la silla del barbero
esta libre en ese momento). Cuando el barbero termina de pelar a un cliente, le indica que puede
levantarse y espera a que le pague; a continuaci
on, si hay clientes esperando en las sillas, el barbero
avisa a uno cualquiera y espera a que se siente en la silla del barbero para comenzar a pelarlo.
Ejercicio propuesto: Completar la plantilla p42.c para obtener un programa Java que simula la
actuacion del barbero y de los clientes que entran en la barbera. Los clientes y el barbero son hebras
y la barbera es un monitor que ha de implementar la sincronizacion entre estos. En el metodo
cortarPelo, ha de programarse la actuacion completa de los clientes correctamente sincronizada. El
barbero llama al metodo siguienteCliente para avisar (notificar) a un cliente sentado en una silla
o para esperar (durmiendo) la llegada de un cliente, si la barbera esta vaca. Cuando termina de
pelar a un cliente, le notifica que puede levantarse y espera hasta que le paga, llamando al metodo
finCliente. El cliente, a su vez, paga y sale de la barbera, lo que ocasiona que el barbero pueda
entonces llamar a siguienteCliente.
import Utilities.*;
class p42{
static int c=400;
static int b= 10;
private static final int NUM_CLIENTES = 4;
public static void main(String[] args){
GetOpt opc= new GetOpt(args, "Uc:b:");
int opcion= -1;
// procesa argumentos
while((opcion = opc.getopt()) != opc.optEOF)
if ((char) opcion == U){
System.out.println("Uso: -c <tiempo de ejecucion de los clientes>");
System.out.println("
-b <tiempo de ejecucion del barbero>");
System.exit(1);
}
else if ((char) opcion == c)
c= opc.processArg(opc.optArgGet(), c);
else if ((char) opcion == b)
b= opc.processArg(opc.optArgGet(), b);
else System.exit(1);
//Declarar instancias del monitor Barberia, de las clases Cliente y Barbero
Barberia b= new Barberia();
// El metodo start() se supone llamado en el constructor
Barbero barbero = new Barbero (b);
Cliente[] clientes= new Cliente[NUM_CLIENTES];

8
// Crearse una instancia de Scheduler para aumentar la replanificacion de las hebras
Scheduler scheduler = new Scheduler(5);
for (int i=0; i <NUM_CLIENTES; i++){
// El metodo start() se supone llamado en el constructor
clientes[i] = new Cliente(b, i);
}
}
}// p42
class Barberia {
// Declarar constantes estaticas que representen los estados del barbero. Por ejemplo:
// private static final int DURMIENDO = 0;
// ...
// las sillas de espera ocupadas
private int sillas= 0;
// el estado del barbero
private int estado = DURMIENDO;
public synchronized void cortarPelo (int id) {
// ...
//Mientras el barbero este "ocupado"(pelando, esperando, etc.), hay que esperar!
//Salvo que le barbero este "durmiendo", un cliente que entre a la barberia ha de sentarse,
//aunque no se puede asegurar que los clientes sentados pasen a pelarse en orden FIFO!
while(estado == AVISANDO)
try{
wait();
} catch(InterruptedException e){
System.out.println("Wait interrumpido " + e.toString());
}
// ...
System.out.println("Cliente <"+id+"> coge una silla y espera");
// ...
System.out.println("Cliente <"+id+"> despierta al barbero");
//

...

// esperar a que acabe el corte de pelo


System.out.println("Cliente <"+id+"> se sienta en la silla del barbero");
// notificar a las hebras.
// ...
System.out.println("Cliente <"+id+">: pago y me voy!");
}
public synchronized void siguienteCliente () {
// ...
System.out.println("El barbero avisa al siguiente cliente");
//

se notifica a 1 cualquiera de los clientes que esperan en las sillas

// ...
// si no hay clientes esperando ...
System.out.println("El barbero se duerme");
// ...
// esperamos a que se siente el cliente
}
}
public synchronized void finCliente () {
// ...
// notificar a las hebras.
System.out.println("El barbero ha terminado el corte de pelo");
// espero a que salga el cliente y me pague
//

...

}
}
class Cliente extends p42 implements Runnable {
private Barberia barberia;
private Thread t;
private int id;
public Cliente(Barberia barberia, int id){
// Completar el codigo del constructor
}
public void run () {
while (true) {
barberia.cortarPelo (id);
try { Thread.sleep (c); } catch (InterruptedException e) { }
}
}
}
class Barbero extends p42 implements Runnable {
private Barberia barberia;
private Thread t;
private int id;
public Barbero(Barberia barberia){
// Completar el codigo del constructor
}
public void run () {
while (true) {
barberia.siguienteCliente ();
try { Thread.sleep (b); } catch (InterruptedException e) { }
barberia.finCliente ();
}
}
}

10
Se pide:
Completar el codigo del monitor Barber
a y de los constructores de las clases Barbero y Cliente.
Compilar y ejecutar el programa, comprobando que no se bloquea y que el comportamiento de
las hebras se corresponda con la actuacion esperada de los clientes y el barbero en la barbera,
tal como se ha descrito en el enunciado del problema del barbero durmiente. Contestar a las
siguientes preguntas:
1. Indica los estados del barbero que has definido como constantes estaticas en tu programa.
2. Por que al final del metodo cortarPelo hay que notificar a todas las hebras que esperan,
si s
olo es necesario informar al barbero de que no tiene que esperar m
as por el pago .
3. Suponemos que hay varios clientes en las sillas de espera y que el barbero acaba de notificar que puede pasar el siguiente (termina de ejecutar siguienteCliente y, por tanto, el
monitor queda libre): como se puede evitar que entre un nuevo cliente en la barbera y se
siente en la silla del barbero, adelantandose as a todos los clientes que estaban esperando.
4. Por que, al final de finCliente, se notifica a todas las hebras y no s
olo a la que representa
al cliente que acaba de ser atendido.
5. Enviar la contestacion a las preguntas anteriores en un archivo de texto, adem
as del archivo
con el programa que se ha pedido.

También podría gustarte