2009-10-29 15 views
7

Q1. ¿Qué es un condVar en Java? Si veo el código a continuación, ¿una variable de condición debe estar necesariamente dentro del bloque 'mutex.acquire()' y 'mutex.release()'?¿Qué es una variable de condición en java?

public void put(Object x) throws InterruptedException { 
    mutex.acquire(); 
    try { 
     while (count == array.length) 
     notFull.await(); 
     array[putPtr] = x; 
     putPtr = (putPtr + 1) % array.length; 
     ++count; 
     notEmpty.signal(); 
    } 
    finally { 
    mutex.release(); 
    } 
} 

I tienen tres hilos myThreadA, myThreadB, myThreadC funcionamiento que llame a la misma función commonActivity() que desencadena la función myWorkReport() por ejemplo

public void myWorkReport(){ 
    mutexMyWork.acquire(); 
    try{ 
     while(runMyWork){ 
      doWork(); 
      conditionMyWork.timedwait(sleepMyWork); 
     } 
    } 
    finally{ 
     mutexMyWork.release() 
    }  
} 

public void commonActivity(){ 
    try{ 
     conditionMyWork.signal(); 
    }finally{ 
     //cleanup 
    } 
} 

public void myThreadA(){ 
    mutexA.acquire(); 
    try{ 
     while(runningA){ //runningA is a boolean variable, this is always true as long as application is running 
      conditionA.timedwait(sleepA); 
      commonActivity(); 
     } 
    } 
    finally{ 
     mutexA.release(); 
    } 
} 


public void myThreadB(){ 
    mutexB.acquire(); 
    try{ 
     while(runningB){ //runningB is a boolean variable, this is always true as long as application is running 
      conditionB.timedwait(sleepB); 
      commonActivity(); 
     } 
    } 
    finally{ 
     mutexB.release(); 
    } 
} 

public void myThreadC(){ 
    mutexC.acquire(); 
    try{ 
     while(runningC){ //runningC is a boolean variable, this is always true as long as application is running. 
      conditionC.timedwait(sleepC); 
      commonActivity(); 
     } 
    } 
    finally{ 
     mutexC.release(); 
    } 
} 

Q2. Está utilizando el tiempo-espera una buena práctica. Pude haber logrado lo mismo usando sleep(). Si el uso de la llamada de suspensión() es malo, ¿por qué?

Q3. ¿Hay alguna forma mejor de hacer lo anterior?

Q4. ¿Es obligatorio tener condition.signal() por cada condición.tiempo de espera (tiempo);

Respuesta

6

Q1) El mejor recurso para esto es probablemente el JavaDoc para la clase Condición. Las variables de condición son un mecanismo que le permite probar que una condición en particular es verdadera antes de permitir que su método continúe. En el caso de su ejemplo, hay dos condiciones, notFull y notEmpty.

El método put se muestra en su ejemplo espera para la condición notFull para convertirse en verdadera antes de intentar añadir un elemento en la matriz, y una vez que la inserción completa que las señales de la condición notEmpty para despertar ningún hilos bloqueados a la espera de eliminar una elemento de la matriz.

... hace una variable de condición necesariamente tienen que estar dentro del 'mutex.acquire) (' y 'mutex.release) (' bloque?

Cualquier llamada a cambiar las variables de condición no necesitan estar dentro de una región sincronizada - esto puede ser a través de la construida en synchronized palabra clave o una de las clases del sincronizador proporcionadas por el paquete java.util.concurrent como Lock. Si no sincroniza las variables de condición, hay dos posibles resultados negativos:

  1. se saltó la señal - aquí es donde un hilo comprueba una condición y lo encuentra no se sostiene, pero antes de que bloquea otro hilo entra en juego, realiza alguna acción para hacer que la condición se vuelva verdadera, y luego señala todos los hilos que esperan la condición. Lamentablemente, el primer hilo ya ha verificado la condición y bloqueará de todos modos, aunque en realidad podría continuar.

  2. El segundo problema es el habitual en el que puede tener varios subprocesos que intenten modificar el estado compartido simultáneamente. En el caso de su ejemplo, múltiples hilos pueden llamar al put() simultáneamente, todos ellos comprueban la condición y observan que la matriz no está llena e intentan insertarse en ella, sobrescribiendo así los elementos en la matriz.

Q2) espera programado puede ser útil para propósitos de depuración, ya que le permite registrar información en el caso de que el hilo no se despierta a través de una señal.

Usando sleep() en lugar de una espera temporizada no es una buena idea, ya que como se mencionó anteriormente es necesario llamar al método await() dentro de una región sincronizada y sleep() no libera los bloqueos mantenidos, mientras await() hace. Esto significa que cualquier hilo que duerme aún mantendrá el (los) bloqueo (s) que han adquirido, causando que otros hilos se bloqueen innecesariamente.

Q4) Técnicamente, no, no es necesario llamar a signal() si está utilizando una espera temporizada, sin embargo, hacer esto significa que todas las esperas no regresará hasta que haya transcurrido el tiempo de espera, que es ineficiente decir El menos.

+0

+1 por qué las variables de condición necesitan mutex.acquire() y release(); – pankajt

0

Q1. Creo que por "variable de condición", se refiere a algo que verifica para determinar la condición en la que esperó. Por ejemplo - si usted tiene la típica situación del productor-consumidor, es posible implementarlo como algo así como:

List<T> list; 

public T get() 
{ 
    synchronized (list) 
    { 
     if (list.get(0) == null) 
     { 
      list.wait(); 
     } 
     return list.get(0); 
    } 
} 

public void put(T obj) 
{ 
    synchronized (list) 
    { 
     list.add(obj); 
     list.notify(); 
    } 
} 

Sin embargo, debido al potencial de despertadores hilo espurios, es posible que el método de los consumidores a salir de la llamada wait() mientras la lista aún está vacía. Por lo tanto, es una buena práctica usar una variable de condición para esperar/dormir/etc. hasta que la condición es verdadera:

while (list.get(0) == null) 
{ 
    list.wait(); 
} 

usando while en lugar de if significa que el método consumidor método consumidor sólo saldrá de ese bloque si definitivamente tiene algo para volver. Hablando en términos generales, cualquier llamada de espera, espera o bloqueo que haya sido desencadenada por una condición y donde espere que cambie la condición, debe estar en un bloque while que verifica esa condición en cada ciclo.

En su situación, ya está haciendo esto con el contenedor while (count == array.length) alrededor de notFull.await().

Q2. La espera temporizada es generalmente una buena práctica: el tiempo de espera le permite realizar periódicamente un control de cordura en su entorno (por ejemplo, se ha volteado un indicador de apagado), mientras que una interrupción no temporizada solo puede detenerse mediante interrupción. Por otro lado, si la espera va a seguir bloqueando de todos modos hasta que la condición sea verdadera, no hay diferencia que se despierte cada 50 ms (por ejemplo) hasta que notify() ocurra 2 segundos más tarde, o si solo bloquea constantemente durante esos 2 segundos.

En cuanto a wait() vs sleep() - el primero es generalmente preferible, ya que significa que se despierta tan pronto como sea capaz de tomar medidas.Thread.sleep(500) significa que este hilo es definitivamente sin hacer nada durante los próximos 500ms, incluso si lo que está esperando está listo 2ms más tarde. obj.wait(500), por otro lado, se habría despertado durante 2 ms y podría continuar su procesamiento. Dado que los sueños introducen retrasos incondicionales en su programa, generalmente son una forma más chabacana de hacer cualquier cosa; solo son adecuados cuando no espera en una condición específica, pero en realidad desea dormir durante un tiempo determinado (por ejemplo, una limpieza) hilo que se dispara cada 60 segundos). Si está "durmiendo" porque está esperando que otro subproceso haga algo primero, use un wait() (u otra técnica síncrona como CountDownLatch).

Q3. Pasar - parece que hay un montón de repeticiones allí, y como el código no tiene ningún comentario en y no has explicado qué se supone que debe hacer y cómo debe comportarse, no voy a intentarlo y aplicar ingeniería inversa a eso de lo que estás escrito. ;-)

0

Q1. Las variables de condición son parte de monitors facility que a veces se utiliza para la sincronización de hilos. No reconozco estas implementaciones particulares, pero generalmente el uso de variables condicionales debe realizarse en la sección crítica, por lo tanto, se requieren mutex.acquire y release.

Q2. timedwait espera la señal en la condición de condición O tiempo de espera y luego requiere sección crítica. Entonces, difiere del sueño.

Q3. No estoy seguro, pero creo que puede usar la funcionalidad integrada de monitores en java: synchronized para exclusión mutua y wait y notify en lugar de cond vars. Por lo tanto, reducirá las dependencias de su código.

3

Q1: Un objeto Condition está asociado (y adquirido) a un objeto Lock (aka mutext). El javadoc para la clase es bastante claro en cuanto a su uso y aplicación. Para esperar en la condición que necesita haber adquirido el bloqueo, y es una buena práctica de codificación hacerlo en un bloque try/finally (como lo ha hecho). Tan pronto como el hilo que ha adquirido el bloqueo espera una condición para ese bloqueo, el bloqueo se abandona (atómicamente).

Q2: El uso de la espera temporizada es necesario para garantizar la vitalidad de su programa en caso de que la condición que está esperando nunca ocurra. Definitivamente es una forma más sofisticada, y es completamente inútil si no verifica el hecho de que ha agotado el tiempo y tomar medidas para manejar la condición de tiempo de espera.

Usando el sueño es una forma aceptable de esperar a que ocurra algo, pero si ya está utilizando un bloqueo ("mutex") y tiene una variable de condición para que el bloqueo, que no tienen sentido de no use the time wait method of the condition:

Por ejemplo, en su código, simplemente está esperando un período determinado, pero NO verifica si se produjo una condición o si se agotó el tiempo de espera. (Eso es un error.) Lo que debe hacer es verificar si su llamada cronometrada fue verdadera o falsa. (Si devuelve falso, se agotó el tiempo de espera & la condición NO se ha producido (todavía)).

public void myThreadA(){ 
    mutexA.acquire(); 
    try{ 
     while(runningA){ //runningA is a boolean variable 
      if(conditionA.await (sleepATimeoutNanos)) 
       commonActivity(); 
      else { 
       // timeout! anything sensible to do in that case? Put it here ... 
      } 
     } 
    } 
    finally{ 
     mutexA.release(); 
    } 
} 

Q3: [editado] Los fragmentos de código requieren un contexto más detallada para ser comprensible. Por ejemplo, no está del todo claro si las condiciones en los hilos son todas iguales (pero supongo que sí).

Si todo lo que intentas hacer es asegurar que commonActivity() se ejecute solo por un hilo a la vez, AND, ciertas secciones de commonActivity() NO requieren control de contención, Y, sí requieres la instalación a tiempo en su espera, entonces, puede simply use a Semaphore. Tenga en cuenta que sempahore tiene su propio conjunto de métodos para timed waits.

Si TODA la commonActivity() es crítica, Y, realmente no le importa esperar (sin tiempos de espera) simplemente haga de commonActivity() un método sincronizado.

[edición final :)] Para ser más formal al respecto, las condiciones se usan normalmente en escenarios donde tiene dos o más hilos cooperando en una tarea y necesita transferencias entre los hilos.

Por ejemplo, tiene un servidor que procesa respuestas asincrónicas a las solicitudes de los usuarios y el usuario está esperando el cumplimiento de un objeto Future. Una condición es perfecta en este caso. La implementación futura está esperando la condición y el servidor señala su finalización.

En los viejos tiempos, usábamos wait() y notify(), pero ese no era un mecanismo muy robusto (o trivialmente seguro). Los objetos de Bloqueo y Condición fueron diseñados precisamente para abordar estas deficiencias.

(A good online resource as a starting point)

Buy and read this book.

+0

gracias alphazero para una explicación tan detallada. myThreadA, myThreadB, myThreadC son totalmente diferentes, pero tienen una sección común llamada commonActivity (puede decir que las tres después de procesar llaman a un método común que imprime registros o informes o eventos). La actividad común ocurre solo si el procesamiento por cualquiera de los hilos es exitoso, de lo contrario no ... por lo que la actividad común se desencadena usando variables de condición. ¿Tengo sentido? – pankajt

+0

De modo que n subprocesos ejecutan n tareas distintas, y si * cualquiera * se procesa, se ejecuta la 'actividad común'. La pregunta permanece: se invocó commonActivity una vez (independientemente de cuántos subprocesos se realizaron correctamente) o, una vez y solo una vez, si alguno de los subprocesos fue exitoso. – alphazero

+0

Todos los hilos myThreadA, myThreadB, myThreadC tienen un ciclo while cuya condición siempre es verdadera (runningA, runningB, runningC son siempre verdaderos siempre que la aplicación esté en funcionamiento. Solo durante el apagado, runningA, runningB, runningC se vuelven falsos). Esto significa que Common Activity es continuamente invocada por los tres Threads continuamente. – pankajt

0

Q1. Creo que documentation da una descripción bastante buena. Y sí, a await o signal debe mantener el bloqueo asociado con la condición.
Q2.timedWait no está en Condition API, está en TimeUnit API. Si usa Condition y desea tener un tiempo de espera para esperar, use await(long time, TimeUnit unit). Y tener un tiempo de espera en general es una buena idea; nadie quiere que un programa se cuelgue para siempre, siempre que sepa qué hacer si se agota el tiempo de espera.
Dormir es para esperar incondicionalmente y esperar es para esperar un evento. Ellos tienen diferentes propósitos. Q3. No sé qué se espera que haga este código. Si desea realizar alguna acción de forma cíclica, con un descanso entre cada iteración, use sleep en lugar de condiciones.
Q4. Como escribí anteriormente las condiciones no tienen el método timedwait, tienen el método await. Y llamar al await significa que quiere esperar a que ocurra algún evento. Esto supone que a veces este evento ocurre y alguien lo señala. ¿Derecha?

Cuestiones relacionadas