2009-07-09 8 views
11

Un peu d'aide s'il vous plaît, considérez le peu de code ci-dessous.Verrouillage réentrant

public class Widget { 
    public synchronized void doSomething() { 
     ... 
    } 
} 

public class LoggingWidget extends Widget { 
    public synchronized void doSomething() { 
     System.out.println(toString() + ": calling doSomething"); 
     super.doSomething(); 
    } 
} 

Je lis que lorsque doSomething() dans LoggingWidget est appelée, la machine virtuelle Java tentera d'acquérir un verrou sur LoggingWidget d'abord, puis sur Widget.

Je suis curieux de connaître la raison. Est-ce parce que la JVM sait que doSomething() a un appel à super.doSomething() ou parce que l'appel d'une méthode de sous-classe acquiert toujours aussi un verrou sur la superclasse.

Vive

+0

Vous devriez publier une référence à l'endroit où vous lisez ceci parce que ce n'est pas vrai :-) –

+0

Merci en effet pour votre aide. J'ai mal compris l'explication du verrouillage réentrant.Après avoir lu votre explication, je suis retourné à la source (un extrait du livre Concurrency en pratique) et cela a du sens maintenant. – CaptainHastings

Répondre

9

Vous vous trompez - les verrous sont obtenus au niveau exemple. Il n'y a qu'un seul verrou dans votre exemple parce qu'il n'y a qu'une seule instance créée lorsque vous dites:

Widget w = new LoggingWidget 

Vous pouvez afficher les verrous (également appelés moniteurs, mutex ou sémaphores) comme étant individuellement " attaché "à chaque instance d'objet dans le JVM.

Si vous aviez une autre méthode synchronized dans la sous-classe LoggingWidget, vous verriez cela comme vrai. Il ne serait pas possible d'appeler cette méthode (nouvelle) et la méthode doSomething en même temps [avec des threads différents sur le même objet].

Cela vaut également pour une autre méthode synchronized sur la superclasse (c'est-à-dire qu'elle n'est affectée en aucune façon par des méthodes qui sont remplacées).

1

Il n'y a qu'une seule instance pour verrouiller le, l'instance de LoggingWidget, il n'y a jamais une instance réelle de Widget.

Le verrou est obtenu lorsque vous appelez LoggingWidget.doSomething() et comme vous avez déjà le verrou lorsque vous appelez super.doSomething() cette méthode est exécutée normalement.

+0

Merci Nick, c'est logique. – CaptainHastings

5
public synchronized void doSomething() { 
    System.out.println(toString() + ": calling doSomething"); 
    super.doSomething(); 
} 

est le même que:

public void doSomething() { 
    synchronized (this) { 
     System.out.println(toString() + ": calling doSomething"); 
     super.doSomething(); 
    } 
} 

Vous verrouillage sur l'instance, et non pas la classe. Donc, quand super.doSomething() est appelé, vous avez déjà cette instance verrouillée. La réentrance fonctionne en acquérant d'abord le verrou.

+0

Merci, comme la chanson va, je peux voir clairement maintenant ... – CaptainHastings

0

Quand un thread acquiert le verrou, il est connu dans le jvm. Lors de la saisie d'un bloc de code synchronisé avec le thread qui détient actuellement un verrou, ils sont autorisés à continuer sans avoir à reprendre le verrou. Le jvm augmente alors un compteur à chaque action réentrante, en diminuant davantage en quittant ce bloc jusqu'à ce que le compte soit zéro. Lorsque le compte est à zéro, le verrou est libéré.

0

B.Goetz - "JJava Concurrency in Practice" si les verrous intrinsèques n'étaient pas réentrants, l'appel à super.doSomething ne pourrait jamais acquérir le verrou car il serait considéré comme étant déjà bloqué, et le thread s'arrêterait de façon permanente pour un verrou, il ne peut jamais acquérir. La réentrance nous évite l'impasse dans des situations comme celle-ci.