web-dev-qa-db-fra.com

Singleton Java et synchronisation

Veuillez clarifier mes questions concernant Singleton et le multithreading:

  • Quel est le meilleur moyen d'implémenter Singleton en Java, dans un environnement multithread ?
  • Que se passe-t-il lorsque plusieurs threads essaient d'accéder à la méthode getInstance() En même temps?
  • Pouvons-nous donner à getInstance()synchronized de singleton?
  • La synchronisation est-elle vraiment nécessaire lors de l'utilisation de classes Singleton?
100
RickDavis

Oui il faut. Vous pouvez utiliser plusieurs méthodes pour atteindre la sécurité des threads avec une initialisation différée:

Synchronisation draconienne:

private static YourObject instance;

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

Cette solution nécessite que chaque thread soit synchronisé alors qu'en réalité, seuls les premiers doivent l'être.

Double vérification de la synchronisation :

private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}

Cette solution garantit que seuls les premiers threads qui tentent d’acquérir votre singleton doivent passer par le processus d’acquisition du verrou.

Initialisation à la demande :

private static class InstanceHolder {
    private static final YourObject instance = new YourObject();
}

public static YourObject getInstance() {
    return InstanceHolder.instance;
}

Cette solution tire parti des garanties du modèle de mémoire Java concernant l'initialisation de la classe pour assurer la sécurité des threads. Chaque classe ne peut être chargée qu'une seule fois et ne sera chargée que si cela est nécessaire. Cela signifie que la première fois que getInstance est appelé, InstanceHolder sera chargé et instance sera créé. Et comme ceci est contrôlé par ClassLoaders, aucune synchronisation supplémentaire n'est nécessaire.

190
Jeffrey

Ce modèle effectue une initialisation lazy-thread-safe de l'instance sans synchronisation explicite!

public class MySingleton {

     private static class Loader {
         static final MySingleton INSTANCE = new MySingleton();
     }

     private MySingleton () {}

     public static MySingleton getInstance() {
         return Loader.INSTANCE;
     }
}

Cela fonctionne car il utilise le chargeur de classes pour effectuer toute la synchronisation pour vous gratuitement: La classe MySingleton.Loader est d'abord accédée dans la méthode getInstance(). La classe Loader est donc chargée lorsque getInstance() est appelé pour la première fois. De plus, le chargeur de classe garantit que toutes les initialisations statiques sont terminées avant que vous n’ayez accès à la classe - c’est ce qui vous garantit la sécurité des threads.

C'est comme par magie.

C'est en fait très similaire au modèle enum de Jhurtado, mais je trouve que le modèle enum est un abus du concept enum (bien que cela fonctionne)

61
Bohemian

Si vous travaillez sur un environnement multithread en Java et que vous devez garantir que tous ces threads accèdent à une seule instance d'une classe, vous pouvez utiliser un Enum. Cela aura l’avantage supplémentaire de vous aider à gérer la sérialisation.

public enum Singleton {
    SINGLE;
    public void myMethod(){  
    }
}

et alors juste que vos threads utilisent votre instance comme:

Singleton.SINGLE.myMethod();
22
jhurtado

Oui, vous devez synchroniser getInstance(). Si ce n'est pas le cas, il peut arriver que plusieurs instances de la classe puissent être créées. 

Prenons le cas où vous avez deux threads qui appellent getInstance() en même temps. Maintenant, imaginez que T1 s’exécute juste après la vérification instance == null, puis que T2 s’exécute. À ce stade, l’instance n’est ni créée ni définie. T2 réussira donc la vérification et créera l’instance. Maintenant, imaginez que l'exécution revienne à T1. Maintenant, le singleton est créé, mais T1 a déjà effectué le contrôle! Il va procéder à faire l'objet à nouveau! Rendre getInstance() synchronisé empêche ce problème.

Il existe plusieurs façons de rendre les singletons thread-safe, mais rendre getInstance() synchronisé est probablement le plus simple.

8
Oleksi

Enum singleton

Le moyen le plus simple d'implémenter un Singleton compatible avec les threads est d'utiliser un Enum.

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Ce code fonctionne depuis l’introduction d’Enum en Java 1.5.

Double vérification du verrouillage

Si vous souhaitez coder un singleton «classique» fonctionnant dans un environnement multithread (à partir de Java 1.5), vous devez utiliser celui-ci.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

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

Ce n'est pas thread-safe avant 1.5 parce que l'implémentation du mot-clé volatile était différente.

Chargement précoce de Singleton (fonctionne même avant Java 1.5)

Cette implémentation instancie le singleton lorsque la classe est chargée et fournit la sécurité du thread.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
6
Dan Moldovan

Vous pouvez également utiliser un bloc de code statique pour instancier l'instance au chargement de la classe et éviter les problèmes de synchronisation des threads.

public class MySingleton {

  private static final MySingleton instance;

  static {
     instance = new MySingleton();
  }

  private MySingleton() {
  }

  public static MySingleton getInstance() {
    return instance;
  }

}
2
usha

Quel est le meilleur moyen d'implémenter Singleton en Java, dans un environnement multithread?

Reportez-vous à cet article pour connaître le meilleur moyen d'implémenter Singleton.

Quel est un moyen efficace d'implémenter un motif singleton en Java?

Que se passe-t-il lorsque plusieurs threads essaient d'accéder à la méthode getInstance () en même temps?

Cela dépend de la façon dont vous avez implémenté la méthode. Si vous utilisez le double verrouillage sans variable volatile, vous pouvez obtenir un objet Singleton partiellement construit.

Référez-vous à cette question pour plus de détails:

Pourquoi l'utilisation de volatile dans cet exemple de verrouillage à double contrôle

Pouvons-nous synchroniser la méthode getInstance () de singleton?

La synchronisation est-elle vraiment nécessaire lors de l'utilisation de classes Singleton?

Non requis si vous implémentez Singleton de différentes manières

  1. intitalisation statique
  2. enum 
  3. LazyInitalaization avec Initialization-on-demand-holder_idiom 

Référez-vous à cette question pour plus de détails

Java Singleton Design Pattern: Questions

0
Ravindra babu
public class Elvis { 
   public static final Elvis INSTANCE = new Elvis();
   private Elvis () {...}
 }

Source: Effective Java -> élément 2 

Il suggère de l'utiliser si vous êtes sûr que la classe restera toujours singleton. 

0
Ashish