Question

Java 5 et supérieure. Supposons un ordinateur multiprocesseur à mémoire partagée (vous utilisez probablement un moment).

Voici un code pour l'initialisation paresseuse d'un singleton:

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

t-instance être déclaré volatile afin d'empêcher l'optimiseur de réécriture getInstance () comme suit (ce qui serait correct dans un programme séquentiel):

public static MySingleton getInstance() {
  if (instance == null) {
    synchronized (MySingleton.class) {
      // instance must be null or we wouldn't be here  (WRONG!)
      instance = new MySingleton();
    }
  }
}

En supposant que l'optimiseur ne réécrit pas le code, si instance n'est pas déclarée volatile est-il encore assuré d'être vidées à la mémoire lorsque le bloc synchronized est sorti et lu de la mémoire lorsque le bloc synchronized est entré?

EDIT: J'ai oublié de faire getInstance () statique. Je ne pense pas que cela change la validité des réponses; vous saviez tous ce que je voulais dire.

Était-ce utile?

La solution

Oui, instance doit être déclarée volatile. Même alors, il est conseillé de ne pas utiliser le verrouillage revérifié. (Ou pour être plus précis, le modèle mémoire de Java) utilisé pour avoir une grave lacune qui a permis la publication d'objets partiellement mis en œuvre. Cela a été corrigé dans Java5, encore DCL est un idiome obsolète et il n'y a pas besoin de l'utiliser plus - utiliser le idiome paresseux de support d'initialisation place.

De Java dans la pratique Concurrency, l'article 16.2:

  

Le vrai problème avec DCL est l'hypothèse que la pire chose qui peut se produire lors de la lecture d'une référence d'objet partagé sans synchronisation est de voir par erreur une valeur périmée (dans ce cas, null); dans ce cas, l'idiome DCL compense ce risque en essayant à nouveau avec le verrou détenu. Mais le pire des cas est en fait bien pire -. Il est possible de voir une valeur actuelle de la référence, mais les valeurs périmées pour l'état de l'objet, ce qui signifie qu'on pouvait voir l'objet d'être dans un état non valide ou incorrect

     

Les variations ultérieures de JMM (Java 5.0 et versions ultérieures) ont permis DCL travailler si resource est fait volatile, et l'impact sur les performances de c'est faible, car volatile lit sont généralement un peu plus cher que lit non volatile. Cependant, ceci est un idiome dont l'utilité est largement passé - les forces qui ont motivé ce (lent synchronisation sans compétition, le démarrage de la machine virtuelle Java lente) ne sont plus en jeu, ce qui rend moins efficace que l'optimisation. L'idiome porte d'initialisation paresseuse offre les mêmes avantages et est plus facile à comprendre.

Autres conseils

Oui, par exemple doit être volatile à l'aide de verrouillage revérifié en Java, car sinon l'initialisation de MySingleton pourrait exposer un objet partiellement construit au reste du système. Il est vrai aussi que les fils se synchronisent quand ils atteignent l'énoncé « synchronisé », mais qui est trop tard dans ce cas.

Wikipedia et quelques autres questions Stack Overflow ont de bonnes discussions de « double- vérifié le verrouillage », donc je vous conseille de lire jusqu'à ce sujet. Je conseille également de ne pas utiliser à moins que le profilage montre un réel besoin de performances dans ce code particulier.

Il y a un langage plus sûr et plus lisible pour l'initialisation paresseuse de Java singletons:

class Singleton {

  private static class Holder {
    static final Singleton instance = create();
  }

  static Singleton getInstance() { 
    return Holder.instance; 
  }

  private Singleton create() {
    ⋮
  }

  private Singleton() { }

}

Si vous utilisez le modèle de verrouillage revérifié plus bavard, vous devez déclarer le volatile sur le terrain, que d'autres ont déjà noté.

Non. Il ne doit pas être volatile. Voir Comment résoudre la Déclaration « verrouillage revérifié est brisé » en Java?

tentatives précédentes ont tous échoué parce que si vous pouvez tricher java et éviter volatile / synchronisation, java peut vous tromper et de vous donner une vision erronée de l'objet. Cependant la nouvelle sémantique de final résout le problème. si vous obtenez une référence d'objet (par lecture ordinaire), vous pouvez lire en toute sécurité ses champs final.

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