Download programación en java - ISA-UMH
Document related concepts
no text concepts found
Transcript
Escuela Politécnica Superior de Elche Ingeniería Industrial SISTEMAS INFORMÁTICOS INDUSTRIALES curso 2007-2008 PROGRAMACIÓN EN JAVA PRÁCTICA 3: Comunicación productor / consumidor. entre tareas. Modelo Objetivos • • • Implementar una aplicación en Java en la que existan varios threads o hilos de ejecución. Conocer el modelo clásico de comunicación entre tareas productor / consumidor. Usar los mecanismos de Java para sincronizar tareas: utilización de los métodos wait() y notify(). Descripción Una aplicación multitarea permite realizar de forma simultánea varias tareas. Estas tareas pueden comunicarse entre sí. Un mecanismo clásico de comunicación de tareas es el modelo productor / consumidor. En este modelo el productor produce una determinada salida, que es adquirida por el consumidor. Para comprender este modelo de comunicación se va a crear una aplicación en Java en la que: - El productor será una tarea encargada de introducir caracteres en una tubería. El consumidor será una tarea que irá leyendo los caracteres que va introduciendo el productor. La propia tubería actuará como un monitor que controlará el proceso de sincronización entre las dos tareas. La tubería consistirá en una clase que contendrá un array de caracteres de un determinado tamaño en el que se introducirán y extraerán caracteres. Será necesario controlar que el productor no introduzca un carácter si el array está lleno o si el consumidor está extrayendo un carácter. Del mismo modo, habrá que controlar que el consumidor no lea un carácter si el array está vacío o si el productor está insertando un carácter. Gráficamente el modelo productor / consumidor se puede representar como: Página 1 de 5 Escuela Politécnica Superior de Elche Ingeniería Industrial Monitor Productor lanzar(char c) recoger() Consumidor ADGHCE Detalles de implementación Productor Para implementar el productor se creará una clase denominada Productor que derivará de la clase Thread. El código de esta clase es el siguiente: // fichero Productor.java class Productor extends Thread { private Tuberia tuberia; private String alfabeto="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public Productor(Tuberia t) { tuberia = t; } public void run() { char c; // introduce 10 caracteres en la tuberia for(int i=0;i<10;i++){ c = alfabeto.charAt((int)(Math.random()*26)); // se introduce el caracter en la tuberia tuberia.lanzar(c); // se espera antes de añadir más caracteres try{ sleep((long)(Math.random()*100)); } catch(InterruptedException e){ e.printStackTrace(); } } } } En el método run() de la clase Productor se puede observar que se introducen 10 caracteres en la tubería utilizando el método tuberia.lanzar(char). Los caracteres son introducidos de uno en uno con una separación temporal aleatoria. Página 2 de 5 Escuela Politécnica Superior de Elche Ingeniería Industrial Consumidor Para implementar el consumidor se creará una clase denominada Consumidor que derivará de la clase Thread. El código de esta clase es el siguiente: // fichero Consumidor.java class Consumidor extends Thread { private Tuberia tuberia; public Consumidor(Tuberia t) { tuberia = t; } public void run() { char c; // consume 10 caracteres de la tuberia for(int i=0;i<10;i++){ c = tuberia.recoger(); // se espera antes de recoger más caracteres try{ sleep((long)(Math.random()*2000)); } catch(InterruptedException e){ e.printStackTrace(); } } } } En este caso, en el método run() se leen 10 caracteres de la tubería utilizando el método tuberia.recoger(). Los caracteres son recogidos de uno en uno con una separación temporal aleatoria. Tubería La clase Tuberia debe controlar la comunicación entre el productor y el consumidor. A continuación se muestra parte del código de esta clase: Página 3 de 5 Escuela Politécnica Superior de Elche Ingeniería Industrial // fichero Tuberia.java class Tuberia { private int tamTuberia; private char []buffer; // indica la posicion libre del buffer private int siguiente; // flags que indican el estado del buffer private boolean estaLlena; private boolean estaVacia; public Tuberia(int tam) { tamTuberia = tam; buffer = new char[tamTuberia]; siguiente = 0; estaLlena = false; estaVacia = true; } // método para retirar letras del buffer public synchronized char recoger() { } // método para añadir letras al buffer public synchronized void lanzar(char c) { } } La variable miembro buffer representa el array en el que el productor introducirá caracteres, y desde el que el consumidor los leerá. El tamaño de este array vendrá dado por tamTuberia. Las variables miembros estaLlena y estaVacia actúan como semáforos. Indicarán, respectivamente, si el buffer de caracteres está lleno o vacío. En esta clase falta por implementar los métodos lanzar() y recoger(). Estos métodos son sincronizados. Esto implica que mientras el productor esté añadiendo una letra a la tubería, el consumidor no la puede retirar, y viceversa. - En el método recoger() será necesario comprobar si el buffer está vacío, ya que en ese caso, el consumidor deberá esperar hasta que el productor introduzca algún carácter. Para que el consumidor se quede en espera, deberá utilizarse el método wait(). El consumidor dejará de esperar cuando se introduzca un carácter en el buffer. Por tanto en el método lanzar(char) será necesario invocar al método notify() para indicar al consumidor que puede continuar su ejecución. Página 4 de 5 Escuela Politécnica Superior de Elche Ingeniería Industrial - En el método lanzar() será necesario comprobar si el buffer está lleno, ya que en ese caso, el productor deberá esperar hasta que el consumidor recoja algún carácter. Por tanto en el método recoger() será necesario invocar al método notify() para indicar al productor que puede continuar su ejecución. Tanto en el método recoger() como en el método lanzar(), deberá mostrarse en pantalla el carácter leído o introducido respectivamente. Aplicación Se debe crear una aplicación que cree una tubería y arranque las dos tareas: el productor y el consumidor. Para ello se puede utilizar el siguiente código: // fichero ComunicacionTareas.java class ComunicacionTareas { public static void main(String []arg) { Tuberia t = new Tuberia(6); Productor p = new Productor(t); Consumidor c = new Consumidor(t); p.start(); c.start(); } } Observaciones Tras implementar los métodos recoger() y lanzar(char) se debe comprobar el correcto funcionamiento de la aplicación. En el fichero fuente de la aplicación se ha creado una tubería cuyo buffer es de 6 caracteres. Comprobar que modificando el tamaño del buffer, así como el número de caracteres que introduce el productor y extrae el consumidor, la aplicación sigue comportándose correctamente. Página 5 de 5