2012-09-25 15 views
23

El código siguiente se toma de la JavaDoc of Condition:espera en una condición en un reentrante bloquear

class BoundedBuffer { 
    final Lock lock = new ReentrantLock(); 
    final Condition notFull = lock.newCondition(); 
    final Condition notEmpty = lock.newCondition(); 

    final Object[] items = new Object[100]; 
    int putptr, takeptr, count; 

    public void put(Object x) throws InterruptedException { 
    lock.lock(); 
    try { 
     while (count == items.length) 
     notFull.await(); 
     items[putptr] = x; 
     if (++putptr == items.length) putptr = 0; 
     ++count; 
     notEmpty.signal(); 
    } finally { 
     lock.unlock(); 
    } 
    } 

    public Object take() throws InterruptedException { 
    lock.lock(); 
    try { 
     while (count == 0) 
     notEmpty.await(); 
     Object x = items[takeptr]; 
     if (++takeptr == items.length) takeptr = 0; 
     --count; 
     notFull.signal(); 
     return x; 
    } finally { 
     lock.unlock(); 
    } 
    } 
} 

Imagínese 2 hilos, Consumer y Productor, uno usando take, uno put en una sola instancia de BoundedBuffer.

Digamos Consumidor va primero, corre take() en la que bloquea la lock y ahora bucles en notEmpty.await();.

¿Cómo puede ahora Productor posiblemente entrar en el método put() pasado el bloqueo de la lock, que ya está en manos de la Consumidor?

¿Qué me falta aquí? ¿Está el lock "liberado temporalmente" mientras el hilo está esperando en una de sus condiciones? ¿Y qué significa la reentrada de la cerradura, exactamente?

+2

estaba a punto de dos preguntan exactamente la misma pregunta con exactamente el mismo ejemplo en JavaDoc :) Me salvó de perder tiempo. – Bren

Respuesta

23

Ambos Lock y synchronized permiten que un hilo suelte el candado mientras espera y otro hilo puede obtener el candado. Para detener la espera, un hilo debe volver a adquirir el bloqueo.

Nota: No lo liberan por completo y si toma un seguimiento de la pila puede tener varios hilos que parecen estar presionando el bloqueo de una vez, pero como máximo uno de ellos se ejecutará (el resto será de bloqueo)

de Condition.await()

el bloqueo asociado con esta condición es atómicamente en libertad y el subproceso actual se incapacita para fines de programación hilo y permanece latente hasta que uno de cuatro cosas sucede:

  • Algún otro subproceso invoca el método signal() para esta condición y el hilo actual se elige como el subproceso que se va a activar; o
  • Algún otro subproceso invoca el método signalAll() para esta condición; o
  • Algún otro hilo interrumpe el hilo actual, y se admite la interrupción de la suspensión de hilo; o
  • Se produce una "activación falsa".

En todos los casos, antes de que este método pueda regresar, el hilo actual debe volver a adquirir el bloqueo asociado con esta condición. Cuando el hilo retorna, se garantiza que mantiene este candado

+0

¿Podría proporcionar un enlace a la documentación relevante? – vektor

+1

@PeterLawrey '(el resto estará esperando) ¿o bloqueo? Creo que deberían estar en estado de bloqueo' – UnKnown

+0

¿Puede explicar esta oración? "Tanto el bloqueo como el sincronizado permiten temporalmente a otros obtener el bloqueo cuando están esperando". No tiene sentido para mí. – Chin

5

En cuanto a la reentrada, esto significa que un hilo que retiene un cierto candado puede volver a adquirir el mismo candado nuevamente. Si esto no fuera así, un método synchronized no podría llamar a otro método synchronized del mismo objeto.

La reentrada no está involucrada en la comprensión de su problema.

+0

Exactamente. A veces también el término "bloqueo recursivo" se usa para bloqueos de reentrada, vea [Wikipedia] (https://en.wikipedia.org/wiki/Reentrant_mutex). – TheOperator

-2

He probado a continuación código con solo monitor y por debajo siempre tiene un mejor rendimiento - Probado en 2 máquina de la base, el rendimiento Condición está por debajo del 10-15% en un promedio

final Object sync = new Object(); 
    AtomicInteger add=new AtomicInteger(); 
    AtomicInteger remove=new AtomicInteger(); 
    final Object[] items = new Object[1]; 
    int putptr, takeptr, count;   

public void add(Object x) throws InterruptedException { 
     add.incrementAndGet(); 

    synchronized (sync) { 

     while (count == items.length) 
     sync.wait(); 
     items[putptr] = x; 
     if (++putptr == items.length) putptr = 0; 
     ++count; 
     sync.notify(); 
     } 
    } 

    public Object remove() throws InterruptedException { 
     remove.incrementAndGet(); 

    synchronized (sync) { 

     while (count == 0) 
     sync.wait(); 
     Object x = items[takeptr]; 
     if (++takeptr == items.length) takeptr = 0; 
     --count; 
     sync.notify(); 
     return x; 

     } 
    } 


    public static void main(String[] args) { 
    final BoundedBuffer bf=new BoundedBuffer(); 

    Thread put =new Thread(){ 
     public void run(){ 
     try { 
      while(true) 
      bf.add(new Object()); 
     } catch (InterruptedException e) { 

     } 
     } 

    }; 
    put.start(); 

    Thread take= new Thread(){ 
     public void run(){ 
     try { 
     while(true) 
      bf.remove(); 
     } catch (InterruptedException e) { 

     } 
     } 

    }; 
    take.start(); 

    try { 
     Thread.sleep(1000L); 
     put.interrupt(); 
     take.interrupt(); 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 


    System.out.println("add:"+bf.add); 
    System.out.println("remove:"+bf.remove); 
Cuestiones relacionadas