java - semaforos - ¿Alguien puede explicar los monitores de hilo y esperar?
semaforos en java pdf (6)
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 de monitor mutex + condition_variable = , la llamada de espera establece la variable de condición en un 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.
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 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 porqué en profundidad 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?
editar: Gran conjunto de respuestas: realmente desearía poder seleccionar más de una porque todas me ayudaron a entender lo que estaba pasando.
Espera abandona el monitor, por lo que debes tenerlo para abandonarlo. Notify también debe tener el monitor.
La razón principal por la que desea hacer esto es asegurarse de tener el monitor cuando regrese de la espera (): normalmente está utilizando el protocolo de esperar / notificar para proteger algunos recursos compartidos y desea que sea seguro. tóquelo cuando la espera vuelva. Lo mismo con notificar: generalmente está cambiando algo y luego llama a notify () - desea tener el monitor, hacer cambios y llamar a notify ().
Si hiciste una función como esta:
public void synchWait() {
syncronized { wait(); }
}
No tendrías el monitor cuando la espera regresara; podrías obtenerlo, pero es posible que no lo obtengas a continuación.
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, lo cual en mi experiencia sí sucede.
Para esperar otro hilo para cambiar una condición a verdadero y notificar:
synchronized(o) {
while(! checkCondition()) {
o.wait();
}
}
Por supuesto, en estos días, recomendaría simplemente usar el nuevo objeto Condition, 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();
}
}
Necesita poseer el monitor, ya que el propósito de la espera () es liberar el monitor y dejar que otros subprocesos obtengan el monitor para que haga su propio procesamiento. 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 inserta datos en 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.
Si el objeto no posee el monitor de objeto 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 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 involuntarias en el método wait ().
Es de suponer que el método wait () no está sincronizado porque podría crear situaciones en las que el subproceso tiene múltiples bloqueos en el objeto. (Consulte Especificaciones del lenguaje Java / Bloqueo para obtener más información al respecto). 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.
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. por ejemplo, una lectura desde el disco ha finalizado y los datos pueden ser procesados 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.
La mayor parte de la espera se realiza si hay una condición que indique que la cola está vacía.
If(queue is empty)
queue.wait();
Supongamos que la cola está vacía. En caso de que si el hilo actual se adelanta luego de verificar la cola, si otro hilo agrega pocos elementos a la cola, el hilo actual no lo sabrá y se ejecutará en estado de espera. Eso está mal. Entonces deberíamos tener algo como
Synchornized(queue)
{
if(queue is empty)
queue.wait();
}
Ahora consideremos qué pasaría si ellos mismos esperaran sincronizados. 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.