Question

Je lisais cet article à propos de " Double -Vérifié le verrouillage " et hors du sujet principal de l'article, je me demandais pourquoi, à un moment de l'article, l'auteur utilisait le prochain idiome:

  

Listing 7. Tentative de résolution du problème d'écriture hors service

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 a-t-il une raison de synchroniser deux fois du code avec le même verrou? Cela a-t-il un but?

Merci d'avance.

Était-ce utile?

La solution

L'intérêt de verrouiller deux fois était de tenter d'empêcher les écritures non conformes. Le modèle de mémoire spécifie où les réorganisations peuvent avoir lieu, 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 vous recommanderais L'article de Bill Pugh . Et puis, ne tentez jamais cela:)

Autres conseils

L'article fait référence au modèle de mémoire Java (JMM) antérieure à la version 5.0. Dans ce modèle, un bloc synchronisé forcé écrit en mémoire principale. Cela semble donc être une tentative pour s'assurer que l'objet Singleton est expulsé avant la référence. Toutefois, cela ne fonctionne pas tout à fait, car l’écriture sur une instance peut être déplacée vers le haut dans le bloc - le motel Roach.

Cependant, le modèle pré-5.0 n'a jamais été correctement implémenté. 1.4 devrait suivre le modèle 5.0. Les classes sont initialisées paresseusement, vous pouvez donc écrire

public static final Singleton instance = new Singleton();

Ou mieux, n'utilisez pas de singletons car ils sont diaboliques.

Jon Skeet a raison: lisez de Bill Pugh article. L'idiome utilisé par Hans 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 faites jamais ni l'un ni l'autre.

Synchronisez l'intégralité de la méthode:

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

Si vous ne récupérez pas cet objet zillion par seconde, l'impact sur les performances est en réalité négligeable.

Suite à la John Skeet recommandation:

  

Cependant, pour approfondir le sujet   Je recommanderais l'article de Bill Pugh. Et   alors n'essayez jamais:)

Et voici la clé pour le deuxième bloc de synchronisation:

  

Ce code met la construction du   Objet d'assistance à l'intérieur d'un intérieur   bloc synchronisé. L'idée intuitive   voici qu'il devrait y avoir une mémoire   barrière au point où   la synchronisation est libérée, et que   devrait empêcher la réorganisation de la   initialisation de l'objet Helper   et l'affectation sur le terrain   assistant.

Donc, en gros, avec le bloc Inner Sync, nous essayons de "tricher". le JMM créant l'instance à l'intérieur du bloc de synchronisation, pour forcer le JMM à exécuter cette allocation avant la fin du bloc de synchronisation. Mais le problème ici est que JMM nous dirige vers le haut et déplace l'assignation qui se trouve avant le bloc de synchronisation à l'intérieur du bloc de synchronisation, ce qui ramène notre problème au début.

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

D'accord, mais l'article disait que

  

Le code du Listing 7 ne fonctionne pas à cause de la définition actuelle du modèle de mémoire. Le langage JLS (Java Language Specification) exige que le code d’un bloc synchronisé ne soit pas déplacé d’un bloc synchronisé. Cependant, cela ne dit pas que le code qui ne se trouve pas dans un bloc synchronisé ne peut pas être déplacé dans un bloc synchronisé.

Il semble également que la machine virtuelle Java effectue la traduction suivante en "pseudo-code". en 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 non écrit après "instance = inst". n'est pas accompli?

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

Depuis Java 5, vous pouvez effectuer le verrouillage à double contrôle en déclarant le champ volatile.

Voir http://www.cs.umd.edu /~pugh/java/memoryModel/DoubleCheckedLocking.html pour une explication complète.

En ce qui concerne cet idiome, il existe un article très judicieux et explicatif:

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

D’autre part, je pense que dhighwayman.myopenid veut dire pourquoi l’auteur a mis un bloc synchronisé faisant référence à la même classe (synchronized (Singleton.class)) dans un autre bloc synchronisé faisant référence à la même classe. Cela peut se produire lorsqu’une nouvelle instance (Singleton inst = instance;) est créée dans ce bloc. Pour garantir la sécurité des threads, il est nécessaire d’écrire une autre synchronisation.

Sinon, je ne vois pas de sens.

Voir le Google Tech Talk sur le modèle de mémoire Java pour une très bonne introduction aux points les plus fins de la JMM. Comme il manque ici, je voudrais également signaler le blog 'Concurrence Java' de Jeremy Mansons. le message sur le le verrouillage à double contrôle (toute personne faisant l'objet de quelque le monde Java semble avoir un article à ce sujet:).

Pour Java 5 et les versions ultérieures, il existe une variante vérifiée par deux qui peut être meilleure que la synchronisation de l’accesseur complet. Ceci est également mentionné dans la Déclaration de verrouillage à double vérification :

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

La principale différence ici réside dans l'utilisation de volatile dans la déclaration de variable. Sinon, cela ne fonctionne pas, et cela ne fonctionne pas avec Java 1.4 ou moins, de toute façon.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top