Programación Avanzada.
Centro Superior de Informática.
Universidad de La Laguna.


Productor/Consumidor.

Se puede trabajar con múltiples flujos de control de forma que los threads sean asíncronos e independientes. Esto es, cada thread contiene todos los datos y métodos que requiere para su ejecución y no necesita recurso o métodos externos.  Sin embargo, exiten muchas situaciones interesantes en las que threads separados que se ejecutan independientemente han de compartir datos y han de tener en cuenta el estado y las actividades del resto de los threads. Un ejemplo de las segundas situaciones son las conocidas como "Productor/Consumidor" en las que un productor genera un flujo de datos que es absorvido por un consumidor.

Una de las principales dificultades de la utilización de threads es el hecho de compartir datos. Cuando se ejecuta un método, es posible que al thread actual se le acabe el tiempo que le han asignado en medio de un método y que otro thread que ejecuta el mismo método modificandole los valores que utiliza.

La aplicación PCTest.java contiene la definición de una clase Productor/Consumidor en la que se instancian dos objetos: uno de tipo Productor y otro de tipo Consumidor. Estos objetos son Threads que se encargan uno de poner un valor y el otro de cogerlo de un Mostrador. El Productor genera un entero entre 0 y 9, lo almacena en el mostrador y lo imprime. El Consumidor al contrario, consume todos los enteros del mostrador (que es exactamente el mismo objeto en el que el productor pone los enteros) tan pronto como están disponibles. Así pues, el productor y el consumidor de este ejemplo comparten los datos a través del objeto Mostrador.
 
 

productor/consumidor

  1. Proponga una implementación de la clase Mostrador, que proporcione métodos que permitan poner (put) y coger (get) un valor contenido en un objeto de tipo mostrador. Compile la aplicación y compruebe la salida.
  2. ¿Cuanto hay que esperar para poner un nuevo valor? Si el productor es más rápido que el consumidor puede ocurrir que genere dos o más números antes de que el consumidor tenga oportunidad de recoger ninguno.
                Productor #1 put: 0
                Productor #1 put: 1
                Productor #1 put: 2
                Productor #1 put: 3
                Productor #1 put: 4
                Productor #1 put: 5
                Productor #1 put: 6
                Productor #1 put: 7
                Productor #1 put: 8
                Productor #1 put: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                Consumidor #1 got: 9
                 
    ¿Cuantas veces se puede coger un valor puesto en el mostrador? Puede ocurrir que el consumidor sea más rápido que el productor y entonces cosume el mismo número más de una vez.
                Productor #1 put: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Consumidor #1 got: 0
                Productor #1 put: 1
                Productor #1 put: 2
                Productor #1 put: 3
                Productor #1 put: 4
                Productor #1 put: 5
                Productor #1 put: 6
                Productor #1 put: 7
                Productor #1 put: 8
                Productor #1 put: 9
    A este tipo de problemas se les denomina Condiciones de Carrera (Race Conditions). Estas condiciones, surgen cuando múltiples threads asíncronos se ejecutan intentando acceder al mismo objeto al mismo tiempo y proporcionan un resultado erróneo.

    Para simular los dos comportamientos anteriores, haga inactivo al productor o al consumir una cierta cantidad de tiempo (aleatoria).

     
    Podemos enunciar los problemas que se nos presentan de la siguiente manera:
  1. Para solucionar el primer problema utilice la palabra reservada "synchronized" para declarar los métodos poner y coger. Esto hace que el acceso al objeto mostrador esté bloqueado para los otros threads.
  1. Para solucionar el segundo problema, los threads también deberían ser capaces de notificar al resto cuando han terminado su trabajo con el objeto compartido. Considere una variable disponible que contenga un true cuando un valor acaba de ser dejado en el mostrador pero no ha sido cogido y false cuando el valor ha sido cogido pero no repuesto.
  1. Compile su aplicación con estas modificaciones y compruebe si funciona.
  1. Para solucionar el segundo problema, falta además de añadir la variable disponible, el que el consumidor espere ( wait() ) hasta que el productor ponga algo en el mostrador y se lo notifique ( notifyAll() ) a todo el mundo. De forma similar, el productor debe esperar antes de reemplazar el valor en el mostrador hasta que el consumidor coja el valor que ya estaba en el mostrador y se lo notifique al resto del mundo.



Bibliografía.

[1] Y. Daniel Liang. "Introduction to Java Programming". QueE&T. 1999
[2] S. Davis. "Aprenda Java ya". McGraw-Hill. Microsoft Press. 1996.
[3] J. Gosling,  H. McGilton. "The Java Language Enviroment". A White Paper. Sun Microsystems. Java Soft. 1996.
[4] D. Flanagan. "Java en pocs Palabras". McGrawHill-O'Reilyy. 1998.
[5] J.F. Macary, C. Nicolas. "Programación Java". Eyrolles.