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