2008-10-01 16 views
11

je lisais this article de « verrouillage revérifié » et hors du sujet principal de l'article, je me demandais pourquoi à un moment donné de l'article, l'auteur utilise la prochaine Idiom:revérifié verrouillage Article

Listing 7. toute tentative de résoudre le hors problème d'ordre d'écriture

public static Singleton getInstance() 
{ 
    if (instance == null) 
    { 
     synchronized(Singleton.class) {  //1 
      Singleton inst = instance;   //2 
      if (inst == null) 
      { 
       synchronized(Singleton.class) { //3 
        inst = new Singleton();  //4 
       } 
       instance = inst;     //5 
      } 
     } 
    } 
    return instance; 
} 

Et ma question est la suivante: y at-il raison de synchroniser un code deux fois avec le même verrou? L'avez-vous pour but?

Merci beaucoup à l'avance.

+0

Il existe une question connexe "[Meilleure implémentation Singleton en Java] (http://stackoverflow.com/questions/70689/best-singleton-implementation-in-java)" –

Répondre

15

Le point de verrouillage deux fois était à tentative pour empêcher les écritures hors de l'ordre. Le modèle de mémoire spécifie où les réordonnances peuvent se produire, en partie en termes de verrous. Le verrou garantit qu'aucune écriture (y compris aucune dans le constructeur singleton) ne semble se produire après le "instance = inst;" ligne.

Cependant, pour approfondir le sujet, je recommanderais Bill Pugh's article. Et puis jamais le tenter :)

+0

Mais vous pourriez utiliser le Java 5 qui fonctionne réellement variante à la fin de l'article. –

+1

@hstoerr: Personnellement, je ne le ferais pas, à moins que je ne trouve que c'est le goulot d'étranglement réel dans un code. C'est trop facile de se tromper. Tout a sa place, bien sûr, mais il serait assez rare pour moi de l'utiliser. –

0

D'accord, mais l'article a déclaré que

Le code dans le Listing 7 ne fonctionne pas à cause de la définition actuelle du modèle de mémoire. La spécification JLS (Java Language Specification) exige que le code d'un bloc synchronisé ne soit pas déplacé hors d'un bloc synchronisé. Cependant, il ne dit pas que le code qui n'est pas dans un bloc synchronisé ne peut pas être déplacé dans un bloc synchronisé.

Et semble aussi que la machine virtuelle Java fait la traduction suivante de "pseudo-code" dans ASM:

public static Singleton getInstance() 
{ 
    if (instance == null) 
    { 
    synchronized(Singleton.class) {  //1 
     Singleton inst = instance;   //2 
     if (inst == null) 
     { 
     synchronized(Singleton.class) { //3 
      //inst = new Singleton();  //4 
      instance = new Singleton();    
     } 
     //instance = inst;    //5 
     } 
    } 
    } 
    return instance; 
} 

Jusqu'à présent, le point de non écrit après la « instance = inst "n'est pas accompli?

Je vais lire maintenant l'article, merci pour le lien.

6

Jon Skeet a raison: lire l'article Bill Pugh's. L'idiome que Hans utilise est la forme précise que ne fonctionnera pas, et ne devrait pas être utilisé.

Ceci est dangereux:

private static Singleton instance; 

public static Singleton getInstance() { 
    if (instance == null) { 
    synchronized(Singleton.class) { 
     if (instance == null) { 
     instance = new Singleton(); 
     } 
    } 
    } 
    return instance; 
} 

Ceci est également dangereux:

public static Singleton getInstance() 
{ 
    if (instance == null) 
    { 
     synchronized(Singleton.class) {  //1 
      Singleton inst = instance;   //2 
      if (inst == null) 
      { 
       synchronized(Singleton.class) { //3 
        inst = new Singleton();  //4 
       } 
       instance = inst;     //5 
      } 
     } 
    } 
    return instance; 
} 

Ne pas faire l'un d'eux, jamais.

Au lieu de cela, synchronisez toute méthode:

public static synchronized Singleton getInstance() { 
     if (instance == null) { 
     instance = new Singleton(); 
     } 
     return instance; 
    } 

À moins que vous récupérez cet objet un milliard de fois par seconde le succès de la performance, en termes réels, est négligeable.

13

L'article fait référence au modèle de mémoire Java (JMM) pré-5.0.Sous ce modèle, laisser un bloc synchronisé forcé écrit dans la mémoire principale. Donc, il semble être une tentative de s'assurer que l'objet Singleton est expulsé avant la référence à celui-ci. Cependant, cela ne fonctionne pas parce que l'écriture sur l'instance peut être déplacée dans le bloc - le motel gardon.

Cependant, le modèle pré-5.0 n'a jamais été implémenté correctement. 1.4 devrait suivre le modèle 5.0. Les cours sont initialisés paresseusement, vous pourriez tout aussi bien écrire

public static final Singleton instance = new Singleton(); 

Ou mieux, ne pas utiliser singletons car ils sont mauvais.

+0

+1 pour les initialiseurs statiques –

0

En ce qui concerne cet idiome il y a un article très souhaitable et clarifier:

http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1

D'autre part, je pense que ce dhighwayman.myopenid moyen est la raison pour laquelle l'écrivain a mis un bloc synchronisé se référant à la même class (synchronized (Singleton.class)) dans un autre bloc synchronisé faisant référence à la même classe. Cela peut arriver lorsqu'une nouvelle instance (Singleton inst = instance;) est créée dans ce bloc et pour garantir la sécurité des threads, il est nécessaire d'en écrire une autre synchronisée.

Sinon, je ne vois aucun sens.

1

Suite à la John Skeet Recommandation:

Cependant, pour aller plus loin dans le sujet je vous recommande l'article de Bill Pugh. Et puis jamais tenter :)

Et voici la clé pour le second bloc de synchronisation:

Ce code met la construction de l'objet Helper l'intérieur d'un bloc synchronisé intérieur. L'idée intuitive est ici qu'il devrait y avoir une barrière mémoire au point où synchronisation est libéré, et que devrait empêcher la remise en ordre de l'initialisation de l'objet Helper et l'attribution à l'aide du champ .

Donc, en gros, avec le bloc de synchronisation interne, nous essayons de « tricher » le JMM création de l'instance à l'intérieur du bloc de synchronisation, pour forcer le JMM à exécuter cette allocation avant le bloc de synchronisation terminé. Mais le problème ici est que le JMM nous dirige vers le haut et déplace l'assigment qui est avant le bloc de synchronisation dans le bloc de synchronisation, ramenant notre problème au début.

C'est ce que j'ai compris de ces articles, vraiment intéressant et encore une fois merci pour les réponses.

0

Voir le Google Tech Talk sur la Java Memory Model pour une introduction très agréable aux subtilités de la JMM. Comme il manque ici, je voudrais aussi signaler Jeremy Mansons blog 'Java Concurrency' esp. le poste sur Double Checked locking (n'importe qui qui est quelque chose dans le monde de Java semble avoir un article sur ceci :).

0

Pour Java 5 et mieux, il existe en fait une variante à double-échelon qui peut être meilleure que la synchronisation de l'ensemble de l'accesseur. Ceci est également mentionné dans le Double-Checked Locking Declaration:

class Foo { 
    private volatile Helper helper = null; 
    public Helper getHelper() { 
     if (helper == null) { 
      synchronized(this) { 
       if (helper == null) 
        helper = new Helper(); 
      } 
     } 
     return helper; 
    } 
} 

La différence clé ici est l'utilisation de volatile dans la déclaration de variable - sinon il ne fonctionne pas, et il ne fonctionne pas en Java 1.4 ou moins, de toute façon.

+0

Oui, cochez la réponse que j'ai sélectionnée comme correcte. –