El lenguaje Java y el sistema de ejecuci�n soportan la sincronizaxi�n de threads mediante el uso de monitores. En general, un monitor est� asociado con un objeto especifico (una condici�n variable) y funciona como un bloqueo para ese dato. Cuando un thread mantiene el monitor para alg�n dato del objeto, los otros threads est�n bloqueados y no pueden ni inspeccionar ni modificar el dato.
Los segmentos de c�digo dentro de programa que acceden al mismo dato dentro de threads concurrentes separados son conocidos como secciones cr�ticas. En el lenguaje Java, se pueden marcar las secciones cr�ticas del programa con la palabra clave synchronized.
Nota: Generalmente, la secci�n cr�ticas en los programas Java son m�todos. Se pueden marcar segmentos peque�os de c�digo como sincronizados.
Sin embargo, esto viola los paradigmas de la programaci�n orientada a objetos y produce un c�digo que es d�ficil de leer y de mantener. Para la mayor�a de los prop�sitos de programaci�n en Java, es mejor utilizar synchronized s�lo a nivel de m�todos.
En el lenguaje Java se asocia un �nico monitor con cada objeto que tiene un m�todo sincronizado. La clase CubbyHole del ejemplo Producer/Consumer de la p�gina anterior tiene dos m�todos sincronizados: el m�todo put(), que se utiliza para cambiar el valor de CubbyHole, y el m�todo get(), que se utiliza para el recuperar el valor actual. As� el sistema asocia un �nico monitor con cada ejemplar de CubbyHole.
Aqu� tienes el c�digo fuente del objeto CubbyHole. Las l�neas en negrita proporcionan la sincronizaci�n de los threads.
class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e) {
}
}
available = false;
notify();
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e) {
}
}
contents = value;
available = true;
notify();
}
}
La clase CubbyHole tiene dos variables privadas: contents, que es el contenido actual de CubbyHole, y la variable booleana available, que indica si se puede recuperar el contenido de CubbyHole. Cuando available es verdadera indica que el Productor ha puesto un nuevo valor en CubbyHole y que el Consumidor todav�a no la ha consumido. El Consumidor s�lo puede consumir el valor de CubbyHole cuando available es verdadera.
Como CubbyHole tiene dos m�todos sincronizados, java proporciona un �nico monitor para cada ejemplar de CubbyHole (incluyendo el compartido por el Productor y el Consumidor). Siempre que el control entra en un m�todo sincronizado, el thread que ha llamado el m�todo adquiere el monitor del objeto cuyo m�todo ha sido llamado. Otros threads no pueden llamar a un m�todo sincronizado del mismo objeto hasta que el monitor sea liberado.
|
Nota:
Los Monitores Java son Re-entrantes. Es decir, el mismo thread puede llamar a un m�todo sincronizado de un objeto para el que ya tiene el monitor, es decir, puede re-adquirir el monitor. |
As�, siempre que el Productor llama al m�todo put() de CubbyHole, adquiere el monitor del objeto CubbyHole, y as� evita que el consumidor pueda llamar al m�todo get() de CubbyHole. (El m�todo wait() libera temporalmente el monitor como se ver� m�s adelante).
public synchronized void put(int value) {
// El productor adquiere el monitor
while (available == true) {
try {
wait();
} catch (InterruptedException e) {
}
}
contents = value;
available = true;
notify();
// El productor libera el monitor
}
Cuando el m�todo put() retorna, el Productor libera el monitor y por lo tanto desbloquea el objeto CubbyHole.
Siempre que el Consumidor llama al m�todo get() de CubbyHole, adquiere el monitor de ese objeto y por lo tanto evita que el productor pueda llamar al m�todo put().
public synchronized int get() {
// El consumidor adquier el monitor
while (available == false) {
try {
wait();
} catch (InterruptedException e) {
}
}
available = false;
notify();
return contents;
// el Consumidor libera el monitor
}
La adquisici�n y liberaci�n del monitor la hace autom�ticamente el sistema de ejecuci�n de Java. Esto asegura que no puedan ocurrir condiciones de competici�n en la implementaci�n de los threads, asegurando la integridad de los datos.
Prueba esto: Elimina las l�neas que est�n en negrita en el listado de la clase CubbyHole mostrada arriba. Recompila el programa y ejecutalo de nuevo. �Qu� sucede? Como no se ha realizado ning�n esfuerzo expl�cito para sicronizar los threads, el Consumidor consume con un abandono temerario y obtiene s�lo una ristra de ceros, en lugar de obtener los enteros entre 0 y 9 exactamente una vez cada uno.