Práctica 1 - Programación Concurrente 3º I.S. Pág: 1/15 Práctica 1. Monitores en Java. Programación Concurrente. 3º I.S. Dpto. Lenguajes y Sistemas Informáticos Escuela Técnica Superior de Ingeniería Informática y Telecomunicación Universidad de Granada Curso 2008/09 1 Código y métodos 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 código o un método sea protegido por el cerrojo del objeto. Para ejecutar un bloque o un método 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ón hace que las hebras tengan que adquirir el cerrojo del objeto para ejecutar el bloque de código sincronizado. synchronized (obj) { // bloque de codigo sincronizado Si todas las secciones críticas están en el código de un ú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 método sea código sincronizado: tipo metodo (... ) { synchronized (this) { //codigo del metodo sincronizado La siguiente construcción es equivalente a la anterior: synchronized tipo metodo (... ) { //codigo del metodo sincronizado
Práctica 1 - Programación Concurrente 3º I.S. Pág: 2/15 Para construir un monitor en Java, creamos una clase con sus métodos sincronizados. De esta forma solamente una hebra podrá ejecutar en cada momento el método 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());
Práctica 1 - Programación Concurrente 3º I.S. Pág: 3/15 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 2 Métodos de espera y notificación. En Java no existe el concepto de variable condición. Podríamos decir que cada monitor en Java tiene una única variable condición anónima. Los monitores de Java implementan la disciplina signal and continue. Los métodos wait(), notify() y notifyall() implementan los mecanismos de espera y notificación de los monitores Java. Estos métodos solamente pueden ser llamados por una hebra cuando ésta posee el cerrojo del objeto, es decir, desde un bloque o un método sincronizado. La invocación al método 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 métodos del objeto. Sin embargo, otros cerrojos poseídos por la hebra suspendida son retenidos por ésta. La invocación al método 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 ésta al estado preparado. La hebra que invocó notify() seguirá ejecutándose dentro del monitor. La hebra señalada deberá adquirir el cerrojo del objeto para poder ejecutarse. Esto significará esperar al menos hasta que la hebra que invocó notify() libere el cerrojo, bien por la ejecución de una llamada a wait(),o bien por la salida del monitor. La hebra señalada no tiene prioridad alguna para ejecutarse en el monitor. Puede ocurrir que, antes de que la hebra señalada pueda volver a ejecutarse, otra hebra adquiera el cerrojo del monitor. La invocación al método 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.
Práctica 1 - Programación Concurrente 3º I.S. Pág: 4/15 3 Simulación de Tráfico con Monitores Java. Versión 1.0 El ejercicio a realizar consiste en desarrollar un Applet Java que gestione una simulación básica de tráfico de vehículos, aplicando la teoría subyacente de mónitores Java tal y como se ha explicado en los puntos anteriores. La simulación (ver Figura 1) consta de los siguientes componentes: Tres carreteras verticales, etiquetadas correspondientemente por 1, 2, 3. Una carretera horizontal, etiquetada con 0, y que corta a perpendicularmente a las tres anteriores. Un conjunto de 2 a 70 (según se seleccione en un parámetro de entrada al Applet) vehículos todos de igual tamaño que circulan por dichas carreteras siempre en el mismo sentido y por carril único: o De derecha a izquierda en la carretera horizontal. o De arriba abajo en las carreteras verticales. Siempre existe el mismo número de vehículos en la simulación, de tal manera que cuando llegan al fin de la correspondiente carretera, vuelven al principio de la misma. No existe la posibilidad de adelantamientos, de tal manera que los vehículos deben de conservar siempre una distancia de seguridad para no chocar, aunque su velocidad va a ser elegida al azar. Así mismo, cada vehículo debe de tener en cuenta a la hora de pasar por un cruce, si el mismo ya está ocupado por otro vehículo, en cuyo caso debe de hacer la señal de stop correspondiente y pararse hasta que el cruce quede libre. La asignación de cada vehículo a una carretera se hace al azar, no obstante el usuario va a poder pinchar, arrastrar y soltar vehículos de una carretera a otra. El usuario va a poder parar y reanudar la simulación con los correspondientes botones dentro del panel gráfico del Applet.
Práctica 1 - Programación Concurrente 3º I.S. Pág: 5/15 Figura 1. Applet Simulación de Vehiculos Para solucionar este ejercicio se propone las plantillas básicas en Java siguientes: SimulationServices: Dentro del package services, es la clase que proporciona los servicios básicos para tratar la simulación de vehículos. Contiene a su vez: o La clase Vehicle para gestionar los correspondientes vehículos. o La matriz con los vehículos que intervienen en la simulación. o Variables para la gestión de las carreteras, que están representadas en la Figura 1. o Variable instancia única de la clase, de tal manera que la misma cumpla el patrón de singleton y solo haya una instancia de la misma. Para conseguir esto, se hace el constructor de la clase privado. o Etc Simulation: Dentro del package applet, es la clase extension de Applet que realiza la simulacion de los vehiculos. Contiene el punto de entrada a la aplicacion Applet (metodo init). Toda la simulacion la gestiona haciendo uso de los servicios de la clase SimulationServices,
Práctica 1 - Programación Concurrente 3º I.S. Pág: 6/15 anteriormente explicada, mediante una unica instancia de dicha clase getinstacesimulation(). Esta clase gestiona todas las acciones posibles sobre el panel de la simulacion: o Finalizar: provoca un cese de la simulación. o Iniciar: reinicia la simulacion, si previamente se ha seleccionado Finalizar. Contiene y gestiona a su vez la siguiente clase: o ThreadVehicle: Thread para cada uno de los vehiculos que intervienen en la simulación. Como comentarios generales, decir que se han proporcionado en las plantillas todas las variables básicas de las clases, las cabeceras de los principales métodos (aunque sin especificar el cualificador synchronized ) y las variables y métodos (código inclusive) para tratar los principales componentes del Applet y de los gráficos (mediante awt).
Práctica 1 - Programación Concurrente 3º I.S. Pág: 7/15 package services; import java.applet.applet; import java.awt.color; import java.awt.dimension; import java.awt.event; import java.awt.graphics; import java.awt.image; SimulationServices es la clase extension de Applet que proporciona los servicios basicos para la aplicacion de simulacion de vehiculos. Contiene a su vez la siguiente clase: - Vehicle: vehiculos @author DNI - Apellidos, Nombre - Grupo @version V1.0 - N.sesion y fecha de la sesion public class SimulationServices extends Applet { Numero de serie de la version private static final long serialversionuid = 1L; Vehicle es al clase que gestiona la funcionalidades de los vehiculos en la simulacion class Vehicle { Coordenada x del vehiculo private double x; Coordenada y del vehiculo private double y; Carretera por las que circula el vehiculo: 0: carretera horizontal (X) 1,2,3: carreteras verticales (Y) private int road; Desplazamiento de la coordenada x del vehiculo en el ultimo movimiento private double dx; Desplazamiento de la coordenada y del vehiculo en el ultimo movimiento private double dy; Etiqueta que identifica el vehiculo private String label; Ancho del vehiculo private int widev;
Práctica 1 - Programación Concurrente 3º I.S. Pág: 8/15 Largo del vehiculo private int longv; Constructor de la clase Vehicle inicializa los componentes de Vehicle public Vehicle() { Guarda la referencia a la instancia unica de la clase de simulacion SimulationServices para cumplir el patron de singleton private static SimulationServices instancesimulation = null; numero de vehiculos que intervienen en la simulacion private int nvehicules; matriz con los vehiculos existentes en la simulacion private Vehicle vehicles[]; cte. base para calculo de la velocidad de los vehiculos (que es al azar) private final double SPEED = 10; cte. que indica el ancho y longitud maximos de los vehiculos (aunque en principio todos van a ser iguales) private final int VEHICLE_WIDTH = 6, VEHICLE_LENGTH = 9; valor del eje x de la posicion de las carretas verticales (Y) road 1 -> centrada en eje x = xposroady[0] road 2 -> centrada en eje x = xposroady[1] road 3 -> centrada en eje x = xposroady[2] por las que circulan los vehiculos private int xposroady[] = new int[3]; valor del eje y de la posicion de la carreta horizontal (X) road 0 -> centrada en eje y = yposroadx por las que circulan los vehiculos private int yposroadx = 200; posiciones de la parte izquierda y derecha de las carreteras verticales (Y) road 1 -> delimitada en eje x incluido [xposleftroady[0],xposrightroady[0]] road 2 -> delimitada en eje x incluido [xposleftroady[0],xposrightroady[1]] road 3 -> delimitada en eje x incluido [xposleftroady[0],xposrightroady[2]] private int xposleftroady[] = new int[3]; private int xposrightroady[] = new int[3]; posiciones de la parte superior e inferior de las carretera horizontal (X)
Práctica 1 - Programación Concurrente 3º I.S. Pág: 9/15 road 0 -> delimitada en eje y incluido [yposbottomroadx,ypostoproadx] private int ypostoproadx = yposroadx + VEHICLE_WIDTH, yposbottomroadx = yposroadx - VEHICLE_WIDTH; posiciones de la parte izquierda y derecha de los cruces de la carretera horizontal (X) con las correspondientes carreteras verticales (Y) road 0 -> cruce con: road 1 - > delimitado en eje x incluido [xposleftcrossroadx[0],xposrightcrossroadx[0]] road 2 - > delimitado en eje x incluido [xposleftcrossroadx[1],xposrightcrossroadx[1]] road 3 - > delimitado en eje x incluido [xposleftcrossroadx[2],xposrightcrossroadx[2]] private int xposrightcrossroadx[] = new int[3]; private int xposleftcrossroadx[] = new int[3]; posiciones de la parte inferior y superior de los cruces de las carreteras verticales (Y) con la correspondiente carreteras horizontal (X) road 1,2,3 -> cruce con: road 0 - > delimitado en eje y incluido [ypostopcrossroady,yposbottomcrossroady] private int ypostopcrossroady = yposroadx + VEHICLE_LENGTH; private int yposbottomcrossroady = yposroadx - VEHICLE_LENGTH; Variable condicion que indica si los cruces estan libres (=0) u ocupados (=1) Cruces de la carretera horizontal con las correspondientes verticales y viceversa road 0 -> cruce con: road 1 -> blockedcross[0] indica si esta libre (=0) u ocupado (=1) road 2 -> blockedcross[1] indica si esta libre (=0) u ocupado (=1) road 3 -> blockedcross[2] indica si esta libre (=0) u ocupado (=1) private int blockedcross[] = new int[3]; Vehiculo dentro de la simulacion que ha sido picado con el raton private Vehicle vehiclepicked; Posiciones x e y del vehiculo antes de ser picado con el raton private double vehiclepickedoldx, vehiclepickedoldy; Constructor de la clase SimulationServices inicializa los componentes de SimulationServices. Se hace privado para seguir el patron de singleton private SimulationServices() { // se crea la matriz de vehiculos de la simulacion for (int i = 0; i < 3; i++) { // se calcula las posiciones eje x carreteras verticales xposroady[i] = 150 (i + 1); // se calcula la porciones correspondientes a los cruces de las // carreteras verticales xposrightcrossroadx[i] = xposroady[i] - VEHICLE_LENGTH; xposleftcrossroadx[i] = xposroady[i] + VEHICLE_LENGTH; // en principio los cruces se marcan como libres blockedcross[i] = 0;
Práctica 1 - Programación Concurrente 3º I.S. Pág: 10/15 // se calculan las posiciones de las carretas verticales for (int i = 0; i < 3; i++) { xposleftroady[i] = xposroady[i] - VEHICLE_WIDTH; xposrightroady[i] = xposroady[i] + VEHICLE_WIDTH; Devuelve la instancia de la simulacion, esto es, la instancia de la clase SimulationServices unica existente. Si dicha instancia no existe la construye, si ya existe la devuelve. Este metodo asegura por tanto que solo va a haber una instancia de la simulacion, que por tanto, va aseguir el patron de singleton. @return instancesimulation unica existente en la simulacion, si no existe la crea public static SimulationServices getinstacesimulation() { Busca el vehiculo identificado por la etiqueta de entrada y si no existe lo anade a la simulacion mediante el metodo addvehicle @param label identifica el vehiculo a buscar @return posicion que ocupa el vehiculo a buscar en la matriz de vehiculos de la simulacion public int findvehicle(string label) { Anade el vehiculo identificado por la etiqueta de entrada a la simulacion (matriz de vehiculos correspondiente) @param label identifica el vehiculo a anadir @return nvehicules numero de vehiculos existentes en la simulacion tras incorporar el vehiculo de entrada private int addvehicle(string label) { // Tener en cuenta como se deben // establecer los valores iniciales del objeto Vehicle: // road = azar entre 0 (horizontal)y 1,2,3 (verticales) // widev,longv = ctes. correspondientes // label = parametro label // Si es la carretera horizontal: // x = 480 + 210 azar // y = yposroadx // Otro caso // x = xposroady[correspondiente a su carretera]; // y = 10 + 100 azar; Se gestiona el movimiento del vehiculo, teniendo en cuenta que debe de guardar una distancia de seguridad con respecto a los demas y que no puede haber choques en los cruces @param i identifica el vehiculo que se va a mover en la matriz de vehiculos public void movevehicle(int i) { // Tener en cuenta: // - Obtencion del desplazamiento del vehiculo considerando la cte. velocidad
Práctica 1 - Programación Concurrente 3º I.S. Pág: 11/15 // como maximo el vehiculo se desplaza 10 en el movimiento actual // Si carretera horizontal // dx = max(-10, -SPEED azar); // Si carretera vertical // dy = min(10, SPEED azar); // - La posicion final de los vehiculos tiene que // contemplar situaciones de fuera de la carretera: // Dimension d = size(); // // Si es una carretera horizontal // Si x < 0 O x > d.width Entonces x = d.width - 10 azar // Si es una carretera vertical // Si y > d.height O y < 0 Entonces y = 10 azar // - Antes de finalizar hay que repintar el panel con repaint(); Responde ante un click con el raton dentro del panel de simulacion seleccionando como vehiculo picado aquel que estas mas cercano al lugar del click. A dicho vehiculo vehiclepicked le pone las correspondietes coordenadas x,y donde esta posicionado el raton (par.entrada). Previamente se guarda las coordenadas x,y del vehiculo seleccionado como picado, por si finalmente tiene que se restaurado a dicha posicion. @param evt objeto evento x posicion x del click del raton y posicion y del click del raton @return true @see java.awt.component#mousedown(java.awt.event, int, int) public boolean mousedown(event evt, int x, int y) { repaint(); return true; Responde ante un arrastre con el raton dentro del panel de simulacion sobre el vehiculo picado, estableciendo como posicion del vehiculo la posicion del raton x,y (par.entrada) @param evt objeto evento x posicion x del raton y posicion y del raton @return true @see java.awt.component#mousedrag(java.awt.event, int, int) public boolean mousedrag(event evt, int x, int y) { repaint(); return true; Responde cuando se suelta el click del raton despues del arrastre sobre el vehiculo picado, incorporando el vehiculo picado a la simulacion con el resto de vehiculos @param evt objeto evento x posicion x del raton
Práctica 1 - Programación Concurrente 3º I.S. Pág: 12/15 y posicion y del raton @return true @see java.awt.component#mouseup(java.awt.event, int, int) public boolean mouseup(event evt, int x, int y) { // A tener en cuenta: // - en principio la posicion del vehiculo es la del raton x,y de entrada // - se comprueba si el vehiculo queda dentro de alguna carretera // si es asi se le cumplimenta al vehiculo sus atributos // correspondientes: carretera, alto, ancho y posicion // centrada correspondientemente en la carretera // - si no queda dentro de ninguna carretera se deja en la posicion // que tenia antes del picado // finalmente establece que ya no hay ningun vehiculo picado repaint(); return true; Variables y Metodos para la gestion de graficos private Image offscreen; private Dimension offscreensize; private Graphics offgraphics; final Color selectcolor = Color.pink; final Color edgecolor = Color.black; final Color vehiclecolor = new Color(250, 220, 100); Pinta un vehiculo en la simulacion @param g objeto grafico n vehiculo a pintar public void paintvehicle(graphics g, Vehicle n) { int x = (int) n.x; int y = (int) n.y; g.setcolor((n == vehiclepicked)? selectcolor : vehiclecolor); int w = n.widev; int h = n.longv; g.fillrect(x - w / 2, y - h / 2, w, h); g.setcolor(color.black); g.drawrect(x - w / 2, y - h / 2, w - 1, h - 1); g.drawstring(".", x - w / 2 + 2, y + h / 2-2); Pinta las carreteras en la simulacion @param g objeto grafico public void paintroad(graphics g) { Dimension d = size(); int kt; g.setcolor(color.gray); for (int k = 1; k < 4; k++) { g.drawline(xposleftroady[k-1], 0, xposleftroady[k-1], yposbottomroadx); g.drawline(xposleftroady[k-1], ypostoproadx, xposleftroady[k-1], d.height);
Práctica 1 - Programación Concurrente 3º I.S. Pág: 13/15 g.drawline(xposrightroady[k-1], 0, xposrightroady[k-1], yposbottomroadx); g.drawline(xposrightroady[k-1], ypostoproadx, xposrightroady[k-1], d.height); if (k == 1) kt = 0; else kt = xposrightroady[k - 2]; g.drawline(kt, ypostoproadx, xposleftroady[k-1], ypostoproadx); g.drawline(kt, yposbottomroadx, xposleftroady[k-1], yposbottomroadx); g.drawline(xposrightroady[2], yposbottomroadx, d.width, yposbottomroadx); g.drawline(xposrightroady[2], ypostoproadx, d.width, ypostoproadx); Actualiza el grafico de la simulacion, pintando cada uno de sus componentes @param g objeto grafico public void update(graphics g) { Dimension d = size(); if ((offscreen == null) (d.width!= offscreensize.width) (d.height!= offscreensize.height)) { offscreen = createimage(d.width, d.height); offscreensize = d; offgraphics = offscreen.getgraphics(); offgraphics.setcolor(getbackground()); offgraphics.fillrect(0, 0, d.width, d.height); // pinta carreteras paintroad(offgraphics); // pinta los coches for (int i = 0; i < nvehicules; i++) { paintvehicle(offgraphics, vehicles[i]); g.drawimage(offscreen, 0, 0, null);
Práctica 1 - Programación Concurrente 3º I.S. Pág: 14/15 package applet; import java.awt.; import java.applet.applet; Simulation es la clase extension de Applet que realiza la simulacion de los vehiculos. Contiene el punto de entrada a la aplicacion Applet (metodo init). Toda la simulacion la gestiona haciendo uso de los sevicios de la clase SimulationServices mediante una unica instancia de dicha clase services.simulationservices.getinstacesimulation() Gestiona todas las acciones posibles sobre el panel de la simulacion: - Finalizar: provoca un cese de la simulacion - Iniciar: reinicia la simulacion, si previamente se ha seleccionado Finalizar Contiene y gestiona a su vez la siguiente clase: - ThreadVehicle: Thread para cada uno de los vehiculos que intervienen en la simulacion @author DNI - Apellidos, Nombre - Grupo @version V1.0 - N.sesion y fecha de la sesion public class Simulation extends Applet { numero de serie de la version private static final long serialversionuid = 1L; ThreadVehicle es al clase que gestiona las Threads de vehiculos en la simulacion. La gestion basica consiste en proceder al movimiento del vehiculo, en tanto no se haya seleccionado Finalizar en el panel del Applet. public class ThreadVehicle extends Thread { numero de coches que entran en juego en la simulacion private int vehiclesnum; Variables que se consideren oportunas punto de entrada al Applet de la simulacion public void init() { // BorderLayout divide el contenedor en cinco partes: norte, sur, este, // oeste y centro setlayout(new BorderLayout()); // se crea objeto de la clase GraphPanel y se incluye la parte centro // del contenedor add("center", services.simulationservices.getinstacesimulation()); // se obtiene del parametro de entrada el numero de coches y como maximo // se toma 70 vehiclesnum = Integer.parseInt(getParameter("vehiclesNum")); vehiclesnum = Math.min(vehiclesNum, 70); // se anaden los coches al panel
Práctica 1 - Programación Concurrente 3º I.S. Pág: 15/15 // se inicia el movimiento de los vehiculos // se anade una panel en el sur con los botones de opciones Panel btpnl = new Panel(); add("south", btpnl); btpnl.add(new Button("Iniciar")); btpnl.add(new Button("Finalizar")); Gestiona las acciones de los eventos que pueden ocurrir en el panel: - Finalizar: provoca un cese de la simulacion - Iniciar: reinicia la simulacion, si previamente se ha seleccionado Finalizar @param evt objeto evento arg objeto argumento @return true public boolean action(event evt, Object arg) { if (((Button) evt.target).getlabel().equals("finalizar")) { // acciones para detener la simulacion else if (((Button) evt.target).getlabel().equals("iniciar")) { // acciones para reiniciar la simulacion return true;