Pregunta

Alguien en el trabajo acaba de preguntar el razonamiento detrás de tener que esperar dentro de un sincronizado.

Honestamente, no puedo ver el razonamiento. Entiendo lo que dicen los javadocs: que el subproceso 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 por qué bastante profundo o tal vez una referencia a un artículo. No pude encontrar uno en un google rápido.

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

editar: Gran conjunto de respuestas. Realmente desearía poder seleccionar más de una porque todas me ayudaron a entender lo que estaba sucediendo.

¿Fue útil?

Solución

Si el objeto no posee el monitor de objeto cuando llama a Object.wait (), no podrá acceder al objeto para configurar un escucha de notificación 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 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 de objetos. 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 no deseadas en el método wait ().

Presumiblemente, el método wait () no está sincronizado porque eso podría crear situaciones en las que el Thread tiene múltiples bloqueos en el objeto. (Consulte Especificaciones / Bloqueo del lenguaje Java para más información sobre esto.) Los bloqueos múltiples son un problema porque el método wait () solo deshacerá un bloqueo. Si el método se sincronizara, garantizaría que solo se destrabaría el bloqueo del método y se dejaría un posible bloqueo externo deshecho. Esto crearía una condición de punto muerto en el código.

Para responder a su pregunta sobre Thread.sleep (), Thread.sleep () no garantiza que se cumpla cualquier condición que esté esperando. El uso de Object.wait () y Object.notify () permite que un programador implemente manualmente el bloqueo. Los subprocesos se desbloquearán una vez que se envíe una notificación de que se ha cumplido una condición. p.ej. Una lectura del disco ha finalizado y el hilo puede procesar los datos. Thread.sleep () requeriría que el programador realice una encuesta si se cumple la condición, y luego volverá a dormir si no se cumple.

Otros consejos

Muchas buenas respuestas aquí ya. Pero solo quiero mencionar aquí que lo otro DEBE HACER cuando se usa wait () es hacerlo en un ciclo que depende de la condición que está esperando en caso de que vea despertos espurios, lo que en mi experiencia sí sucede.

Para esperar a que algún otro hilo cambie una condición a verdadera y notifique:

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

Por supuesto, en estos días, recomendaría simplemente usar el nuevo objeto 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, un horario / interrupción más flexible, etc. ).

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

}

Necesita ser dueño del monitor, ya que el propósito de wait () es liberar el monitor y dejar que otros hilos obtengan el monitor para procesarlo por su cuenta. El propósito de estos métodos (esperar / notificar) es coordinar el acceso a los bloques de código sincronizado entre dos hilos que requieren entre sí para realizar alguna funcionalidad. No se trata simplemente de garantizar que el acceso a una estructura de datos sea seguro para subprocesos, sino de coordinar eventos entre múltiples subprocesos.

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

Esperar le da el monitor, por lo que debe tenerlo para dejarlo. Notificar también debe tener el monitor.

La razón principal por la que desea hacer esto es para asegurarse de que tiene el monitor cuando regresa de wait (); por lo general, está utilizando el protocolo de espera / notificación para proteger algunos recursos compartidos y desea que lo haga. asegúrese de tocarlo cuando regrese la espera. Lo mismo ocurre con la notificación: por lo general, está cambiando algo y luego llamando a notificar (), desea tener el monitor, realizar cambios y llamar a notificar ().

Si realizó una función como esta:

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

No tendría el monitor cuando la espera regresara; podría obtenerlo, pero es posible que no lo obtenga a continuación.

Aquí entiendo 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 , el wait establece la variable de condición en un estado de espera y libera el mutex. La variable de condición es estado compartido, por lo que debe bloquearse para evitar condiciones de carrera entre subprocesos que desean esperar y subprocesos que desean notificar. En lugar de introducir otro mutex para bloquear su estado, se usa el mutex existente. En Java, el mutex está bloqueado correctamente cuando el subproceso de espera posee el monitor.

En general, la espera se realiza si hay una condición que indica que una cola está vacía.

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

Supongamos que la cola está vacía. En caso de que el subproceso actual se vacíe después de verificar la cola, entonces si otro el hilo agrega algunos elementos a la cola, el hilo actual no lo sabrá y esperará estado. Eso está mal. Entonces deberíamos tener algo como

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

Ahora consideremos qué pasaría si se hicieran esperar como sincronizados. Como ya se mencionó en uno de los comentarios, solo libera 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 irá a esperar con el bloqueo para la cola.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top