web-dev-qa-db-fra.com

Quelle est la signification de "ReentrantLock" en Java?

La réentrance signifie que les verrous sont acquis par thread plutôt que par invocation.

Puisqu'un verrou intrinsèque est détenu par un thread, cela ne signifie-t-il pas qu'un thread exécuté une fois est égal à une base d'invocation?

Merci, cela semble signifier que: dans un thread, si j'obtiens un verrou lockA lorsque la fonction de processus doA qui appelle la fonction doB, et doB également besoin d'un verrou lockA, alors il y aura une réentrance. En Java, ce phénomène est acquis par thread, donc je n'ai pas besoin de considérer les blocages?

42
znlyj

La réentrance signifie que les verrous sont acquis par thread plutôt que par invocation.

C'est une définition trompeuse. C'est vrai (en quelque sorte), mais il manque le vrai point.

La réentrance signifie (dans la terminologie générale CS/IT) que vous faites quelque chose, et pendant que vous le faites encore, vous le faites à nouveau. Dans le cas des verrous, cela signifie que vous faites quelque chose comme ça sur un seul thread:

  1. Acquérir un verrou sur "foo".
  2. Faire quelque chose
  3. Acquérir un verrou sur "foo". Notez que nous n'avons pas libéré le verrou que nous avons précédemment acquis.
  4. ...
  5. Déverrouiller le verrou sur "foo"
  6. ...
  7. Déverrouiller le verrou sur "foo"

Avec un mécanisme de verrouillage/verrouillage réentrant, la tentative d'acquérir le même verrou réussira et incrémentera un compteur interne appartenant au verrou. La serrure ne sera libérée que si le détenteur actuel de la serrure l'a relâchée deux fois.

Voici un exemple dans Java utilisant des verrous/moniteurs d'objets primitifs ... qui sont réentrants:

Object lock = new Object();
...
synchronized (lock) {
    ...
    doSomething(lock, ...)
    ...
}

public void doSomething(Object lock, ...) {
    synchronized (lock) {
        ...
    }
}

L'alternative au réentrant est le verrouillage non réentrant, où ce serait une erreur pour un thread de tenter d'acquérir un verrou qu'il détient déjà.

L'avantage d'utiliser des verrous réentrants est que vous n'avez pas à vous soucier de la possibilité d'échec en raison de l'acquisition accidentelle d'un verrou que vous détenez déjà. L'inconvénient est que vous ne pouvez pas supposer que rien que vous appelez ne changera l'état des variables que le verrou est conçu pour protéger. Cependant, ce n'est généralement pas un problème. Les verrous sont généralement utilisés pour se protéger contre les changements d'état simultanés effectués par les threads other.


Je n'ai donc pas besoin de considérer les blocages?

Oui.

Un thread ne se bloquera pas contre lui-même (si le verrou est réentrant). Cependant, vous pouvez obtenir un blocage si d'autres threads peuvent avoir un verrou sur l'objet que vous essayez de verrouiller.

64
Stephen C

Imaginez quelque chose comme ça:

function A():
   lock (X)
       B()
   unlock (X)

function B():
    A()

Maintenant, nous appelons A. Ce qui se passe:

  • Nous entrons A, verrouillant X
  • Nous entrons B
  • Nous entrons à nouveau dans A, verrouillant à nouveau X

Comme nous n'avons jamais quitté la première invocation de A, X est toujours verrouillé. C'est ce qu'on appelle la rentrée - alors que la fonction A n'est pas encore revenue, la fonction A est appelée à nouveau. Si A s'appuie sur un état global et statique, cela peut provoquer un `` bogue de rentrée '', où avant que l'état statique soit nettoyé de la sortie de la fonction, la fonction est exécutée à nouveau et les demi-valeurs calculées entrent en collision avec le début de le deuxième appel.

Dans ce cas, nous rencontrons un verrou que nous détenons déjà. Si la serrure est consciente de la rentrée, elle se rendra compte que nous sommes déjà le même fil tenant la serrure et nous laisse passer. Sinon, il se bloquera pour toujours - il attendra un verrou qu'il détient déjà.

En Java, lock et synchronized sont sensibles à la rentrée - si un verrou est détenu par un thread, et que le thread tente de réacquérir le même verrou, il est autorisé. Donc, si nous écrivions le pseudocode ci-dessus en Java, cela ne serait pas un blocage.

17
Patashu

Concurrence Java dans les états du livre d'exercices - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

Permettez-moi d'expliquer ce que cela signifie exactement. Tout d'abord, les verrous intrinsèques sont rentrants par nature. La réentrance est obtenue en maintenant un compteur pour le nombre de verrous acquis et le propriétaire de la serrure. Si le nombre est 0 et qu'aucun propriétaire ne lui est associé, cela signifie que le verrou n'est détenu par aucun thread. Lorsqu'un thread acquiert le verrou, la JVM enregistre le propriétaire et définit le compteur sur 1. Si le même thread tente d'acquérir à nouveau le verrou, le compteur est incrémenté et lorsque le thread propriétaire existe, le compteur de blocs synchronisés est décrémenté. Lorsque le nombre atteint 0, le verrouillage est à nouveau libéré.

Un exemple simple serait -

public class Test {
    public synchronized void performTest() {
       //...
    }
}

public class CustomTest extends Test {
    public synchronized void performTest() {
       //...
       super.performTest();
    }
}

sans réentrance, il y aurait une impasse.

enter image description here

9
Aniket Thakur

La réentrance signifie que les verrous sont acquis par thread plutôt que par invocation.

Permettez-moi d'expliquer cela avec un exemple.

class ReentrantTester {

    public synchronized void methodA() {
      System.out.println("Now I am inside methodA()");
      methodB();
    }

    public synchronized void methodB() {
      System.out.println("Now I am inside methodB()");
    }

    public static void main(String [] args) {
        ReentrantTester rt = new ReentrantTester();
        rt.methodA();  
    }

}

Le résultat est:

Now I am inside methodA()
Now I am inside methodB()

Comme dans le code ci-dessus, le ReentrantTester contient deux méthodes synchronisées: methodA () & methodB () La première méthode synchronized methodA () appelle l'autre méthode synchronized methodB ().

Lorsque l'exécution entre dans la méthode A (), le thread en cours acquiert le moniteur de l'objet ReentrantTester. À présent, lorsque methodA () appelle methodB (), car methodB () est également synchronisé, le thread tente à nouveau d'acquérir le même moniteur. Parce que Java prend en charge les moniteurs réentrants, cela fonctionne. Le thread actuel acquiert à nouveau le moniteur du ReentrantTester et continue l'exécution de la méthodeA () et de la méthode B ().

Le runtime Java permet à un thread de réacquérir un moniteur qu'il détient déjà, car Java sont réentrants. Ces moniteurs rentrants sont importants car ils éliminent la possibilité de un thread unique se bloquant sur un moniteur qu'il détient déjà.

2
Jeewantha Samaraweera

Cela signifie simplement qu'une fois qu'un thread a un verrou, il peut entrer dans la section verrouillée du code autant de fois qu'il le faut. Donc, si vous avez une section de code synchronisée telle qu'une méthode, seul le thread qui a atteint le verrou peut appeler cette méthode, mais peut l'appeler autant de fois qu'il le souhaite, y compris tout autre code détenu par le même verrou. Ceci est important si vous avez une méthode qui appelle une autre méthode et que les deux sont synchronisées par le même verrou. Si ce n'était pas le cas, le. Le deuxième appel de méthode se bloquerait. Cela s'appliquerait également aux appels de méthode récursifs.

public void methodA()
{
     // other code
     synchronized(this)
     {
          methodB();
     } 
}

public void methodB()
{
     // other code
     syncrhonized(this)
     {
          // it can still enter this code    
     }

}
2
rubixibuc