Contenedor.java
/**
* @(#)Contenedor.java
*
* La sincronización que permite a Productor y Consumidor operar correctamente, es decir,
* la que hace que Consumidor espere hasta que haya un dato disponible,
* y que productor no genere uno nuevo hasta que haya sido consumido está esta clase.
*
* La sincronización se lleva a cabo pues usando los métodos wait() y notify()/notifiAll().
*
* Es importante el manejo de la bandera para que no se este pidiendo y almacenando el tiempo simultaneamente,
* ya que podría generar resultados inesperados.
*
* Por medio de 'synchronized' adquiere un bloqueo en el objeto que se ejecuta el método que impide que nadie más
* inicie la ejecución en ese objeto de otro método que también esté declarado como 'synchronized'.
* Cuando comienza el método put() se bloquea el objeto de tal forma que si alguien intenta invocar el método get()
* o put() (ambos son synchronized) quedará en espera hasta que el bloqueo se libere (cuando termine la ejecución
* del método). Este mecanismo garantiza que los objetos compartidos mantienen la consistencia.
*
* - Es responsabilidad del programador pensar y gestionar los bloqueos.
* - Los métodos synchronized son más costosos en el sentido de que adquirir y liberarlos bloqueos consume tiempo
* (este es el motivo por el que no están sincronizados por defecto todos los métodos).
**/
package com.revol.hiloSincronizado;
public class Contenedor {
private int dato; // Contiene el valor, que se almacena con put() y se devuelve con get()
private boolean hayDato = false; // Flag (bandera) que indica si el objeto tiene el dato o no.
// En este método put() antes de almacenar el valor en la variable dato debe asegurarse que el valor anterior ha sido consumido.
public synchronized void put(int valor) {
while (hayDato == true) { // Si todavía hay valor se suspende la ejecución del hilo mediante el método wait().
try {
/**
* Se suspende el hilo indefinidamente hasta que se le envìe una 'señal' con el método notify() o notifyAll()
* Cuando la señalización mediante notify() lo produce el método get(), el método continua, asume que el dato
* ya fue consumido y posteriormente...
**/
wait();
} catch (InterruptedException e) {
}
}
// Almacena el valor en dato y notifica que hay un dato disponible.
dato = valor;
hayDato = true;
notifyAll();
}
// En este método get() chequea si hay un dato disponible (no lo hay si hayDato es falso)
public synchronized int get() {
while (hayDato == false) {
try {
/**
* Si no hay dato espera hasta que le avisen.
* Una vez notificado desde el método put(), posteriormente...
**/
wait();
} catch (InterruptedException e) {
}
}
// Cambia el valor de la bandera y devuelve el valor. Pero antes, notifica que el dato ya ha sido consumido y que se puede almacenar otro.
hayDato = false;
notifyAll();
return dato;
}
}
Productor.java
/**
* @(#)Productor.java
*
* El productor no se preocupa si el dato ya ha sido consumido o no. Simplemente lo coloca en el contenedor.
**/
package com.revol.hiloSincronizado;
public class Productor extends Thread {
private Contenedor contenedor; // Almacena los datos que se van produciendo.
public Productor(Contenedor c) {
this.contenedor = c;
setPriority(MAX_PRIORITY);
}
public void run() {
for (int i = 0; i < 10; i++) {
contenedor.put(i); // Se almacena el dato en el contenedor.
System.out.println("Productor. put: " + i);
try {
sleep((int) (Math.random() * 100)); // Espera una cantidad de tiempo aleatoria (hasta 100 milisegundos)
} catch (InterruptedException e) {
}
}
}
}
Consumidor.java
/**
* @(#)Consumidor.java
*
* El consumidor no se preocupa si el dato ya está disponible en el contenedor o no.
**/
package com.revol.hiloSincronizado;
public class Consumidor extends Thread {
private Contenedor contenedor;
public Consumidor(Contenedor c) {
this.contenedor = c;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = contenedor.get(); // Recupera el dato para mostrarlo en consola.
System.out.println("Consumidor. get: " + value);
}
}
}
ProductorConsumidorTest.java
/**
* @(#)ProductorConsumidorTest.java
*
* Hay ocasiones que distintos hilos en un programa sí necesitan establecer alguna relación entre sí,
* o compartir objetos. Se necesita entonces algún mecanismo que permita sincronizar hilos, así como,
* establecer unas 'reglas del juego' para acceder a recursos (objetos) compartidos.
*
* Un ejemplo típico en que dos procesos necesitan sincronizarse es el caso en que un hilo produzca
* algún tipo de información que es procesada por otro hilo.
*
* Al primer hilo le denominaremos productor y al segundo, consumidor.
*
* Los objetos Productor y Consumidor utilizan el recurso compartido Contenedor.
**/
package com.revol.hiloSincronizado;
public class ProductorConsumidorTest {
public static void main(String args[]) {
Contenedor c = new Contenedor();
Productor prod = new Productor(c);
Consumidor cons = new Consumidor(c);
prod.start();
cons.start();
}
}
Resultado:

Curso básico de Java 2015
0 Comentarios
para escribir tu comentario