Curso básico de Java 2015

Toma las primeras clases gratis

COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

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:

new.jpg

Curso básico de Java 2015

Toma las primeras clases gratis

COMPARTE ESTE ARTÍCULO Y MUESTRA LO QUE APRENDISTE

0 Comentarios

para escribir tu comentario

Artículos relacionados