Question

Tout en discutant d'une synchronisation Java question , quelqu'un a fait un commentaire selon lequel les extraits suivants ne sont pas équivalents (et peuvent être compilés en différents bytecodes):

public synchronized void someMethod() {
  //stuff
}

et

public void someMethod() {
  synchronized (this) {
    //stuff
  }
}

Sont-ils équivalents?

Était-ce utile?

La solution

Ils ont une fonction équivalente, bien que les compilateurs que j'ai testés (Java 1.6.0_07 et Eclipse 3.4) génèrent un bytecode différent. Le premier génère:

// access flags 33
public synchronized someMethod()V
  RETURN

Le second génère:

// access flags 1
public someMethod()V
  ALOAD 0
  DUP
  MONITORENTER
  MONITOREXIT
  RETURN

(Merci à ASM pour l'impression du bytecode).

La différence entre eux persiste donc jusqu'au niveau du bytecode et il appartient à la JVM de rendre son comportement identique. Cependant, ils ont le même effet fonctionnel - voir le exemple dans la spécification du langage Java.

Il convient de noter que si la méthode est substituée dans une sous-classe, elle n'est pas nécessairement synchronisée - il n'y a donc aucune différence à cet égard.

J'ai également exécuté un test pour bloquer un thread essayant d'accéder au moniteur dans chaque cas afin de comparer l'apparence de leurs traces de pile dans un vidage de thread. Elles contenaient toutes deux la méthode en question, il n'y a donc aucune différence.

Autres conseils

J'ai fait le commentaire original selon lequel les déclarations sont identiques.

Dans les deux cas, la première chose à faire est que le thread appelant tente d'acquérir le moniteur de l'objet actuel (ce qui signifie this ').

Je ne connais pas de code différent, je serai heureux d’entendre la différence. Mais dans la pratique, ils sont identiques à 100%.

EDIT: , je vais clarifier cela, car certaines personnes ici se sont trompées. Considérez:

public class A {
    public synchronized void doStuff()
    {
        // do stuff
    }
}

public class B extends A {
    public void doStuff()
    {
        // do stuff
        // THIS IS OVERRIDE!
    }
}

Dans ce cas, doStuff () de la classe B remplace toujours doStuff () de la classe A même s'il n'est pas synchronisé.

Le mot clé synchronisé ne fait jamais partie du contrat! Pas pour les sous-classes, pas pour les interfaces, pas pour les classes abstraites.

J'ai fait le commentaire original. Mon commentaire était qu’ils étaient logiquement équivalents, mais que compilions sous un code différent .

Je n’ai rien ajouté d’autre pour justifier cela à l’époque car il n’ya pas grand-chose à justifier vraiment - ils se contentent de compiler en un autre bytecode. Si vous déclarez une méthode comme synchronized , cette synchronisation fait partie de la définition de la méthode. Un bloc synchronisé dans une méthode ne fait pas de la définition de la méthode , mais implique plutôt des codes octets distincts pour acquérir et libérer le moniteur, comme l’a illustré l’une des affiches ci-dessus. Strictement parlant, ce sont des choses légèrement différentes, bien que en ce qui concerne la logique générale de votre programme , elles soient équivalentes .

Quand est-ce important? Eh bien, sur la plupart des machines virtuelles de bureau modernes, presque jamais. Mais par exemple:

  • une machine virtuelle pourrait en principe procéder à des optimisations dans un cas mais pas dans l'autre
  • il existe certaines optimisations du compilateur JIT dans lesquelles le nombre de bytecodes dans la méthode est pris comme critère pour déterminer quelles optimisations doivent être réalisées
  • une VM sans compilateur JIT (certes peu de nos jours, mais peut-être sur un appareil mobile plus ancien?) aura plus de codes bytec à traiter à chaque appel

Oui. L'utilisation du mot clé synchronized sur une méthode d'instance utilise 'this' comme moniteur. Son utilisation également sur une méthode de classe (méthode statique) utilise l'objet Class de la classe (Foo.class).

De cette façon, vous pouvez synchroniser des méthodes entières et, dans le même temps, le synchroniser avec un extrait de code d'une autre méthode en utilisant le style de bloc synchronisé.

Je ne vois aucune différence fonctionnelle: les deux synchronisent leur corps de méthode complet sur (this). Comment la personne qui a commenté que ces différences sont-elles justifiées?

Ils ne sont pas tout à fait équivalents en fonction. Un autre code pourrait utiliser la réflexion pour voir si votre méthode utilise le modificateur synchronisé, mais il n’ya aucun moyen de savoir si une méthode contient un bloc synchronisé sans lire son code intermédiaire.

La possibilité de déterminer si une méthode est synchronisée est parfois utile. Personnellement, j'ai utilisé cet indicateur pour éviter un verrouillage redondant lors de la synchronisation dans la programmation orientée aspect.

MyObject myObjectA;
MyObject myObjectB;

public void someMethod() {
  synchronized (this) {
    //stuff
  }
}

public void someMethodA() {
  synchronized (myObjectA) {
    //stuff
  }
}

public void someMethodB() {
  synchronized (myObjectB) {
    //stuff
  }
}

Dans ce cas:

  • someMethod bloque la classe entière
  • someMethodA bloque myObjectA uniquement
  • someMethodB bloque monObjetB uniquement
  • someMethodA et someMethodB peuvent être appelés en même temps
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top