Los m�todos get() y put() del objeto CubbyHole hacen uso de los m�todos notify() y wait() para coordinar la obtenci�n y puesta de valores dentro de CubbyHole. Los m�todos notify() y wait() son miembros de la clase java.lang.Object.
Nota:
Los m�todos notify() y wait() pueden ser invocados s�lo desde dentro de un m�todo sincronizado o dentro de un bloque o una sentencia sincronizada. |
Investiguemos el uso del m�todo notify() en CubbyHole mirando el m�todo get().
�El m�todo notify()
El m�todo get() llama al m�todo notify() como lo �ltimo que hace (junto retornar). El m�todo notify() elige un thread que est� esperando el monitor poseido por el thread actual y lo despierta. Normalmente, el thread que espera capturar� el monitor y proceder�.
El caso del ejemplo Productor/Consumidor, el thread Consumidor llama al m�todo get(), por lo que el m�todo Consumidor posee el monitor de CubbyHole durante la ejecuci�n del m�todo get(). Al final del m�todo get(), la llamada al m�todo notify() despierta al thread Productor que obtiene el monitor de CubbyHole y procede.
public synchronized int get() { while (available == false) { try { wait(); } catch (InterruptedException e) { } } available = false; notify(); // lo notifica al Productor return contents; }
Si existen varios threads esperando por un monitor, el sistema de ejecuci�n Java elige uno de esos threads, sin ning�n compromiso ni garant�a sobre el thread que ser� eligido.
El m�todo put() trabaja de un forma similar a get(), despertanto al thread consumidor que est� esperando que el Productor libere el monitor.
La clase Object tiene otro m�todo --notifyAll()-- que despierta todos lo threads que est�n esperando al mismo monitor. En esta Situaci�n, los threads despertados compiten por el monitor. Uno de ellos obtiene el monitor y los otros vuelven a esperar.
�El m�todo wait()
El m�todo wait() hace que el thread actual espere (posiblemente para siempre) hasta que otro thread se lo notifique o a que cambie un condici�n. Se utiliza el m�todo wait() en conjunci�n con el m�todo notify() para coordinar la actividad de varios threads que utilizan los mismos recursos.
El m�todo get() contiene una sentencia while que hace un bucle hasta que available se convierte en true. Si available es false -- el Productor todav�a no ha producido un nuevo n�mero y el consumidor debe esperar -- el m�todo get() llama a wait().
El bucle while contiene la llamada a wait(). El m�todo wait() espera indefinidamente hasta que llegue una notificaci�n del thread Productor. Cuando el m�todo put() llama a notify(), el Consumidor despierta del estado de espera y contin�a con el bucle. Presumiblemente, el Productor ya ha generado un nuevo n�mero y el m�todo get() cae al final del bucle y procede. Si el Productor no ha generado un nuevo n�mero, get() vuelve al principio del bucle y continua espeando hasta que el Productor genere un nuevo n�mero y llame a notify().
public synchronized int get() { while (available == false) { try { wait(); // espera una llamada a notify() desde el Productor } catch (InterruptedException e) { } } available = false; notify(); return contents; }
El m�todo put() trabaja de un forma similar, esperando a que el thread Consumidor consuma el valor actual antes de permitir que el Productor genere uno nuevo.
Junto a la versi�n utilizada en el ejemplo de Productor/Consumidor, que espera indefinidamente una notificaci�n, la clase Object contiene otras dos versiones del m�todo wait().
- wait(long timeout)
- Espera una notificaci�n o hasta que haya pasado el tiempo de espera --timeout se mide en milisegundos.
- wait(long timeout, int nanos)
- Espera una notificaci�n o hasta que hayan pasado timeout milisegundos mas nanos nanosegundos.
�Los Monitores y los M�todos notify() y wait()
Habras observado un problema potencial en los m�todos put() y get() de CubbyHole. Al principio del m�todo get(), si el valor de CubbyHole no est� disponible (esto es, el Productor no ha generado un nuevo n�mero desde la �ltima vez que el Consumidor lo consumi�), luego el Consumidor espera a que el Productor ponga un nuevo n�mero en CubbyHole. Aqu� est� la cuesti�n -- �c�mo puede el Productor poner un nuevo valor dentro de CubbyHole si el Consumidor tiene el monitor? (El Consumidor posee el monitor de CubbyHole porque est� dentro del m�todo get() que est� sincronizado).
Similarmente, al principio del m�todo put(), si todav�a no se ha consumido el valor, el Productor espera a que el Consumidor consuma el valor del CubbyHole. Y de nuevo llegamos a la cuesti�n -- �C�mo puede el consumidor obtener el valor de CubbyHole, si el Productor posee el monitor? (El productor posee el monitor de CubbyHole porque est� dentro dentro del m�todo put() que est� sincronizado).
Bien, los dise�adores del lenguaje Java tambi�n pensaron en esto. Cuando el thread entra en el m�todo wait(), lo que sucede al principio de los m�todos put() y get, el monitor es liberado autom�ticamente, y cuando el thread sale del m�todo wait(), se adquiere de nuevo el monitor. Esto le da una oportunidad al objeto que est� esperando de adquirir el monitor y, dependiendo, de qui�n est� esperando, consume el valor de CubbyHole o produce un nuevo valor para el CubbyHole.