threads thread practice example concurrent java multithreading concurrency

java - thread - Condición frente a mecanismo de notificación de espera



thread implementation in java (5)

@AfterWorkGuinness

¿No deberías establecer usedData = verdadero / falso antes de señalizar

Después de que el código de la señal vaya hasta el final del bloque de bloqueo y suéltelo, para que el orden no importe

¿Cuál es la ventaja de usar interfaces / implementaciones de Condición sobre el mecanismo de notificación de espera convencional? Aquí cito los comentarios escritos por Doug Lea:

La condición factoriza los métodos del monitor de objetos (esperar, notificar y notificar a todos) en objetos distintos para dar el efecto de tener múltiples conjuntos de espera por objeto, combinándolos con el uso de implementaciones de bloqueo arbitrarias. Cuando un bloqueo reemplaza el uso de métodos y declaraciones sincronizados, una condición reemplaza el uso de los métodos del monitor de objetos.

Veo que esta es una forma más orientada a objetos de implementar mecanismos de espera / notificación. ¿Pero hay una buena ventaja sobre la anterior?


El mayor problema es que wait / notify es propenso a errores para los nuevos desarrolladores. El principal problema es no saber cómo manejarlos correctamente, lo que puede dar como resultado un error oscuro.

  • si llama a notify () antes de wait (), se pierde.
  • a veces puede no estar claro si se invocan notify () y wait () en el mismo objeto.
  • No hay nada en espera / notificación que requiera un cambio de estado, sin embargo, esto es obligatorio en la mayoría de los casos.
  • wait () puede regresar falsamente

Condición envuelve esta funcionalidad en un componente dedicado, sin embargo, se comporta de manera muy similar.

Hay una pregunta con respecto a esperar / nofity publicado minutos antes de este y muchos, muchos más Buscar [java] + esperar + notificar


Hay muchas ventajas como las mencionadas anteriormente sobre la Interfaz de Condición, algunas importantes son las siguientes:

La interfaz de condición viene con dos métodos adicionales que son:

1) boolean awaitUntil (Fecha límite) lanza InterruptedException: hace que el hilo actual espere hasta que se señalice o se interrumpa, o transcurra el plazo especificado.

2) awaitUninterruptibly (): hace que el hilo actual espere hasta que se señalice.

Si el estado interrumpido de la secuencia actual se establece cuando ingresa a este método, o se interrumpe mientras se espera, continuará esperando hasta que se le envíe una señal. Cuando finalmente regrese de este método, su estado interrumpido aún se establecerá.

Los dos métodos anteriores no están presentes en el monitor predeterminado que está en la clase de objeto, en algunas situaciones queremos establecer la fecha límite para que el hilo espere, entonces podemos hacer eso mediante la interfaz de Condición.

En algunas situaciones, no queremos que se interrumpa el hilo y queremos que el hilo actual espere hasta que se indique, entonces podemos ir al método awaitUninterruptibly presente en la interfaz de condiciones.

Para más información Condición Interface Java Documentation:

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html#awaitUntil%28java.util.Date%29


Para abordar específicamente por qué tener múltiples waitsets es una ventaja:

Con wait / notify, hay diferentes cosas que los subprocesos están esperando (el ejemplo común es una cola de bloqueo de tamaño fijo, con algunos subprocesos colocando elementos en la cola y bloqueando cuando la cola está llena y otros subprocesos desde la cola y bloqueando cuando la cola está vacía), si utiliza notify, lo que hace que el planificador seleccione un hilo del conjunto de espera para notificar, puede tener casos de esquina donde el hilo elegido no está interesado en ser notificado para una situación particular. Por ejemplo, la cola notificará para agregar algo a la cola, pero si el hilo elegido es un productor y la cola está llena, no puede actuar sobre esa notificación, que usted preferiría haber visitado. Con el bloqueo intrínseco debe usar notifyAll para asegurarse de que las notificaciones no se pierdan.

Pero notifyAll incurre en churn con cada llamada, donde cada subproceso se activa y lucha por el bloqueo, pero solo uno puede progresar. Los otros hilos topan por el bloqueo hasta que, uno a la vez, pueden adquirir el bloqueo y lo más probable es que vuelvan a esperar. Genera mucha controversia por poco beneficio, sería preferible poder usar notify y saber que solo se notifica un thread, donde la notificación es relevante para ese thread.

Aquí es donde tener condiciones separadas para esperar es una gran mejora. La cola puede invocar señal en una condición y saber que activará solo un hilo, donde ese hilo espera específicamente la condición.

El documento de la API para la condición tiene un ejemplo de código que muestra el uso de múltiples condiciones para un buffer delimitado, dice:

Nos gustaría seguir esperando poner hilos y tomar hilos en conjuntos de espera por separado para que podamos utilizar la optimización de solo notificar un único hilo en un momento cuando los elementos o espacios estén disponibles en el búfer.


Cuando usa Condition: await()/signal() puede distinguir qué objeto o grupo de objetos / hilos obtiene una señal específica. Aquí hay un pequeño ejemplo en el que algunos hilos, los productores, obtendrán la señal isEmpty , mientras que los consumidores obtendrán la señal de isFull :

private volatile boolean usedData = true;//mutex for data private final Lock lock = new ReentrantLock(); private final Condition isEmpty = lock.newCondition(); private final Condition isFull = lock.newCondition(); public void setData(int data) throws InterruptedException { lock.lock(); try { while(!usedData) {//wait for data to be used isEmpty.await(); } this.data = data; isFull.signal();//broadcast that the data is now full. usedData = false;//tell others I created new data. }finally { lock.unlock();//interrupt or not, release lock } } public void getData() throws InterruptedException{ lock.lock(); try { while(usedData) {//usedData is lingo for empty isFull.await(); } isEmpty.signal();//tell the producers to produce some more. usedData = true;//tell others I have used the data. }finally {//interrupted or not, always release lock lock.unlock(); } }