[código] Problema Productor-Consumidor: sincronización de Hilos en Java (2)
Continuando en el tema de los hilos ... En el siguiente ejemplo veremos el uso e implementación de la sincronización de hilos en Java. De nuevo vamos a tratar el ejemplo del Productor-Consumidor (antes lo hicimos sin sincronización), esta vez con los subprocesos sincronizados (es recomendable que te hayas leido la anterior entrada).
La clase Bufer es la misma de la del ejemplo anterior:
// La interfaz Bufer especifica los métodos llamados por el Productor y el Consumidor.
public interface Bufer {
public void establecer( int valor ); // colocar valor en Bufer
public int obtener(); // devolver valor de Bufer
}
Como segunda medida crearemos la clase BuferSincronizado.java, que implementa la clase Bufer.java, y en la cual se declaran los métodos estableces(int) y obtener() con el prefijo synchronized.
Lo que se hace en estos casos es poner algo así como un semáforo. Para ello usamos una variable llamada cuentaBuferOcupado, que tomará dos posibles valores: 1 si el buffer está ocupado, y 0 si el buffer está vacio.
Fijemonos entonces en el método establecer, en donde hay un ciclo que se repite siempre y cuando el bufer esté lleno (while(cuentaBuferOcupado ==1)). Si esta cnodición se cumple, se muestra un mensaje y se invoca al método wait(), para poner al subproceso en espera.
De lo contrario, es decir, cuando el bufer está vacio, se escribe un dato en el bufer y se actualiza la variable cuentaBuferOcupado (indicando que el bufer está ahora ocupado). Posteriormente se usa el método notify(), el cual avisa a los subprocesos que están en espera que pase al estado “listo”.
De igual forma, el método obtener(), usa la variable cuentaBuferOcupado para determinar cuando puede o no tomar un valor del bufer.
// BuferSincronizado sincroniza el acceso a un solo entero compartido.
public class BuferSincronizado implements Bufer
{
private int bufer = -1; // compartido por los subprocesos productor y consumidor
private int cuentaBuferOcupado = 0; // cuenta de búferes ocupados
// colocar valor en búfer
public synchronized void establecer( int valor )
{
// obtener nombre del subproceso que llamó a este método, para mostrarlo en pantalla
String nombre = Thread.currentThread().getName();
// mientras no haya ubicaciones vacías, colocar subproceso en estado de espera
while ( cuentaBuferOcupado == 1 ) {
// mostrar información del subproceso y del búfer, después esperar
try {
System.err.println( nombre + " trata de escribir." );
mostrarEstado( "Bufer lleno. " + nombre + " espera." );
wait();
}
// si se interrumpió el subproceso en espera, imprimir el rastreo de la pila
catch ( InterruptedException excepcion ) {
excepcion.printStackTrace();
}
} // fin de instrucción while
bufer = valor; // establecer nuevo valor de bufer
// indicar que el productor no puede almacenar otro valor
// sino hasta que el consumidor recupere el valor actual del búfer
++cuentaBuferOcupado;
mostrarEstado( nombre + " escribe " + bufer );
notify(); // indicar al subproceso en espera que entre al estado listo
} // fin del método establecer; se libera el bloqueo en BuferSincronizado
// devolver valor de bufer
public synchronized int obtener()
{
// obtener nombre del subproceso que llamó a este método, para mostrarlo en pantalla
String nombre = Thread.currentThread().getName();
// mientras no haya datos que leer, colocar subproceso en estado de espera
while ( cuentaBuferOcupado == 0 ) {
// mostrar información del subproceso y del búfer, después esperar
try {
System.err.println( nombre + " trata de leer." );
mostrarEstado( "Bufer vacio. " + nombre + " espera." );
wait();
}
// si se interrumpió el subproceso en espera, imprimir el rastreo de la pila
catch ( InterruptedException excepcion ) {
excepcion.printStackTrace();
}
} // fin de instrucción while
// indicar que el productor puede almacenar otro valor
// ya que el consumidor acaba de recuperar el valor de bufer
--cuentaBuferOcupado;
mostrarEstado( nombre + " lee " + bufer );
notify(); // indicar al subproceso en espera que esté listo para ejecutarse
return bufer;
} // fin del método obtener; libera bloqueo en BuferSincronizado
// mostrar la operación actual y el estado del búfer
public void mostrarEstado( String operacion )
{
StringBuffer lineaSalida = new StringBuffer( operacion );
lineaSalida.setLength( 40 );
lineaSalida.append( bufer + "\t\t" + cuentaBuferOcupado );
System.err.println( lineaSalida );
System.err.println();
}
} // fin de la clase BuferSincronizado
La salida de este programa es la siguiente:
# java PruebaBuferCompartido2
Operacion Bufer Cuenta ocupado
Estado inicial -1 0
Productor escribe 1 1 1
Consumidor lee 1 1 0
Productor escribe 2 2 1
Productor trata de escribir.
Bufer lleno. Productor espera. 2 1
Consumidor lee 2 2 0
Productor escribe 3 3 1
Consumidor lee 3 3 0
Consumidor trata de leer.
Bufer vacio. Consumidor espera. 3 0
Productor escribe 4 4 1
Productor termino de producir.
Terminando Productor.
Consumidor lee 4 4 0
Consumidor leyo valores, dando un total de: 10.
Terminando Consumidor.
Como puedes ver, en este caso si el productor intenta escribir un dato en el bufer, pero este está ocupado, espera a que el consumidor lea el dato y desocupe el bufer; y viceversa.









yop12 dice:
Enero 1st, 2009 a las 12:21 am
bien un codigo q m servira debido a los hilos y la sincronizacion , gracias por la aportacion
saludos ( :