web-dev-qa-db-fra.com

En attente d'une condition dans une serrure réentrante

Le code suivant est extrait du JavaDoc de Condition :

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();
    }
  } 
}

Imaginez 2 threads, Consumer et Producer , un utilisant take, un put sur une seule instance de BoundedBuffer.

Disons que Consumer va d'abord, exécute take() dans laquelle il verrouille la lock et boucle maintenant sur la fonction notEmpty.await();.

Comment maintenant Producer peut-être entrer dans la méthode put() après avoir verrouillé le lock, qui est déjà détenu par le Consommateur ?

Qu'est-ce que j'oublie ici? Le lock est-il "temporairement libéré" pendant que le thread attend une de ses conditions? Et que signifie exactement la réentrée du verrou?

28
vektor

Lock et synchronized permettent à un thread d'abandonner le verrou en attendant et un autre thread peut obtenir le verrou. Pour arrêter d'attendre, un thread doit réacquérir le verrou.

Remarque: Ils ne le libèrent pas complètement et si vous prenez une trace de pile, vous pouvez avoir plusieurs threads qui semblent tenir le verrou à la fois, mais au plus l'un d'entre eux s'exécutera (le reste sera bloquant)

De Condition.await ()

Le verrou associé à cette condition est libéré de manière atomique et le thread actuel est désactivé à des fins de planification des threads et reste inactif jusqu'à ce qu'une des quatre choses se produise:

  • Un autre thread appelle la méthode signal () pour cette condition et le thread actuel se trouve être choisi comme le thread à réveiller; ou
  • Un autre thread appelle la méthode signalAll () pour cette condition; ou
  • Un autre thread interrompt le thread actuel et l'interruption de la suspension du thread est prise en charge; ou
  • Un "réveil parasite" se produit.

Dans tous les cas, avant que cette méthode puisse retourner, le thread actuel doit réacquérir le verrou associé à cette condition. Lorsque le fil revient, il est garanti de maintenir ce verrou

28
Peter Lawrey

En ce qui concerne la rentrée, cela signifie qu'un thread qui détient un certain verrou peut à nouveau acquérir le même verrou. Si ce n'était pas le cas, une méthode synchronized ne pourrait pas appeler une autre méthode synchronized du même objet.

La rentrée n'est pas impliquée dans la compréhension de votre problème.

6
Marko Topolnik