2008-10-22 16 views
33

Alguien en el trabajo acaba de pedir el razonamiento detrás de tener que esperar dentro de un sincronizado.¿Alguien puede explicar los monitores de hilo y esperar?

Honestamente, no puedo ver el razonamiento. Entiendo lo que dicen los javadocs: que el hilo debe ser el propietario del monitor del objeto, pero ¿por qué? ¿Qué problemas previene? (Y si es realmente necesario, ¿por qué el método de espera no puede obtener el monitor en sí?)

Estoy buscando un tema en profundidad por qué o quizás una referencia a un artículo. No pude encontrar uno en Google rápido.

Ah, también, ¿cómo se compara thread.sleep?

corregir: Gran conjunto de respuestas - Realmente me gustaría poder seleccionar más de una porque todas me ayudaron a entender lo que estaba pasando.

Respuesta

3

Si el objeto no posee el supervisor de objetos cuando llama a Object.wait(), no podrá acceder al objeto para configurar un detector de escucha hasta que se libere el monitor. En cambio, se tratará como un hilo que intenta acceder a un método en un objeto sincronizado.

O, para decirlo de otra manera, no hay ninguna diferencia entre:

public void doStuffOnThisObject() 

y el siguiente método:

public void wait() 

Ambos métodos se bloquearán hasta que se libere el monitor objeto. Esta es una característica en Java para evitar que el estado de un objeto sea actualizado por más de un hilo. Simplemente tiene consecuencias involuntarias en el método wait().

Presumiblemente, el método wait() no está sincronizado porque podría crear situaciones en las que el subproceso tiene bloqueos múltiples en el objeto. (Consulte Java Language Specifications/Locking para obtener más información sobre esto). Los bloqueos múltiples son un problema porque el método wait() solo deshará un bloqueo. Si el método se sincronizara, se garantizaría que solo se desharía el bloqueo del método al mismo tiempo que se deja sin abrir un bloqueo externo potencial. Esto crearía una condición de interbloqueo en el código.

Para responder a su pregunta en Thread.sleep(), Thread.sleep() no garantiza que se cumpla la condición que está esperando. El uso de Object.wait() y Object.notify() permite a un programador implementar manualmente el bloqueo. Los hilos se desbloquearán una vez que se envíe una notificación de que se ha cumplido una condición. p.ej. Se finalizó una lectura desde el disco y los datos pueden procesarse por el hilo. Thread.sleep() requeriría que el programador sondee si se cumplió la condición, y luego volverá a dormir si no lo ha hecho.

+0

Entiendo sincronizado. La pregunta era, ¿por qué object.wait() necesita tener el monitor, o por qué la adquisición de ese monitor no puede ser encapsulada dentro del objeto?método wait() (¿o es eso lo que hace Thread.sleep()? –

+0

Lo siento, no lo entendí. La respuesta se ha actualizado. ¿Eso responde mejor a tu pregunta? – 64BitBob

+1

Sí, eso tiene mucho sentido, aunque la pregunta abierta es Todavía "¿por qué el método de espera no puede sincronizarse solo?" –

4

Tiene que ser el propietario del monitor, ya que el propósito de la espera() es liberar el monitor y permitir que otros subprocesos obtengan el propio procesamiento del monitor. El propósito de estos métodos (esperar/notificar) es coordinar el acceso a bloques de código sincronizados entre dos hilos que se requieren mutuamente para realizar alguna funcionalidad. No se trata simplemente de garantizar que el acceso a una estructura de datos sea seguro para la tarea, sino también de coordinar eventos entre múltiples hilos.

Un ejemplo clásico sería un caso productor/consumidor en el que un hilo envía datos a una cola y otro hilo consume los datos. El hilo consumidor siempre requerirá que el monitor acceda a la cola, pero liberará el monitor una vez que la cola esté vacía. El hilo del productor solo tendría acceso para escribir en el hilo cuando el consumidor ya no está procesando. Notificaría al hilo del consumidor una vez que haya introducido más datos en la cola, de modo que pueda recuperar el monitor y acceder a la cola nuevamente.

+0

Buen punto, pero ¿qué pasa con el caso en el que simplemente desea esperar en una notificación de otro hilo, no liberar un monitor para que otro hilo pueda tenerlo. tu dices "Threa d.sleep "es más apropiado para este uso? –

+0

En ese caso, probablemente debería crear otro monitor para sincronizar entre esos dos hilos. –

+0

Si no se requiere el intercambio de estructuras de datos, pero simplemente está esperando que ocurra un evento, puede ser suficiente una suspensión, pero el hilo del evento tendría que hacer una interrupción para activar el hilo inactivo (a menos que sea temporizado). Lo que significa que necesitaría acceder al objeto de hilo. – Robin

5

Wait abandona el monitor, por lo que debe tenerlo para abandonarlo. Notify también debe tener el monitor.

La razón principal por la que desea hacer esto es para asegurarse de tener el monitor cuando regrese de wait(): normalmente está utilizando el protocolo wait/notify para proteger algún recurso compartido y lo desea para estar seguro de tocarlo cuando vuelva la espera. Lo mismo con notificar: generalmente está cambiando algo y luego llama a notify() - desea tener el monitor, hacer cambios y llamar a notify().

Si ha realizado una función como esta:

public void synchWait() { 
    syncronized { wait(); } 
} 

usted no tendría que esperar el monitor cuando regresó - usted podría conseguirlo, pero puede que no obtenga lo siguiente.

2

Aquí está mi entendimiento sobre por qué la restricción es realmente un requisito. Estoy basando esto en una implementación de monitor C++ que hice hace un tiempo combinando un mutex y una variable de condición.

En un sistema mutex + condition_variable = monitor, la llamada wait establece la variable de condición en estado de espera y libera el mutex. La variable de condición es de estado compartido, por lo que debe estar bloqueada para evitar condiciones de carrera entre los subprocesos que desean esperar y los subprocesos que desean notificar. En lugar de introducir otro mutex para bloquear su estado, se utiliza el mutex existente. En Java, el mutex está bloqueado correctamente cuando el hilo de esperar es propietario del monitor.

13

Muchas buenas respuestas aquí ya. Pero solo quiero mencionar aquí que el otro DEBE HACER cuando use wait() es hacerlo en un bucle dependiendo de la condición que está esperando en caso de que esté viendo activaciones espúreas, que en mi experiencia sí ocurren.

que esperar algún otro hilo para cambiar a una condición verdadera y notificar:

synchronized(o) { 
    while(! checkCondition()) { 
    o.wait(); 
    } 
} 

Por supuesto, en estos días, lo recomiendo sólo usar el nuevo objeto de condición, ya que es más claro y tiene más características (como permitir múltiples condiciones por bloqueo, poder verificar la longitud de la cola de espera, programar/interrumpir más flexible, etc.).

Lock lock = new ReentrantLock(); 
Condition condition = lock.newCondition(); 
lock.lock(); 
try { 
    while (! checkCondition()) { 
    condition.await(); 
    } 
} finally { 
    lock.unlock(); 
} 

}

2

Parcialmente espera ha hecho si hay una condición dicen una cola está vacía.

If(queue is empty) 
    queue.wait(); 

Vamos a suponer que la cola está vacía. En caso de que el subproceso actual se adelanta luego de verificar la cola, si otro subproceso agrega algunos elementos a la cola, el subproceso actual no se conocerá y se ejecutará en estado de espera . Eso está mal. así que deberíamos tener algo como

Synchornized(queue) 
{ 
    if(queue is empty) 
      queue.wait(); 
} 

Ahora consideremos lo que si se hizo esperar como sincronizado. Como ya se mencionó en uno de los comentarios, libera solo un bloqueo. Eso significa que si wait() se sincronizó en el código anterior, solo se habría liberado un bloqueo. Implica que el hilo actual va a esperar con el bloqueo de la cola.

Cuestiones relacionadas