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.