with - singleton java openclassroom




Qu'est-ce qu'un moyen efficace d'implémenter un pattern singleton en Java? (20)

Assurez-vous que vous en avez vraiment besoin. Faites un google pour "singleton anti-pattern" pour voir quelques arguments contre. Il n'y a rien de foncièrement mauvais avec cela, je suppose, mais c'est juste un mécanisme pour exposer des ressources / données globales, alors assurez-vous que c'est la meilleure façon. En particulier, j'ai trouvé l'injection de dépendance plus utile, surtout si vous utilisez également des tests unitaires car DI vous permet d'utiliser des ressources simulées à des fins de test.

Qu'est-ce qu'un moyen efficace d'implémenter un pattern singleton en Java?


Différentes façons de créer un objet singleton:

  1. Selon Joshua Bloch - Enum serait le meilleur.

  2. vous pouvez également utiliser le double contrôle de verrouillage.

  3. Even inner static class can be used.


J'utilise Spring Framework pour gérer mes singletons. Il n'applique pas le "singleton-ness" de la classe (ce que vous ne pouvez pas vraiment faire si plusieurs chargeurs de classes sont impliqués) mais fournit un moyen très simple de construire et de configurer différentes usines pour créer différents types d'objets.


Je dirais Enum Singleton

Singleton utilisant enum en Java est généralement un moyen de déclarer enum singleton. Enum singleton peut contenir une variable d'instance et une méthode d'instance. Par souci de simplicité, notez également que si vous utilisez une méthode d'instance que vous avez besoin d'assurer la sécurité des threads de cette méthode si cela affecte l'état de l'objet.

L'utilisation d'une énumération est très facile à mettre en œuvre et n'a aucun inconvénient en ce qui concerne les objets sérialisables, qui doivent être contournés des autres manières.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Vous pouvez y accéder par Singleton.INSTANCE , beaucoup plus facilement que d'appeler la méthode getInstance() sur Singleton.

1.12 Sérialisation des constantes enum

Les constantes d'énumération sont sérialisées différemment des objets sérialisables ou extériorisables ordinaires. La forme sérialisée d'une constante enum consiste uniquement en son nom; les valeurs de champ de la constante ne sont pas présentes dans le formulaire. Pour sérialiser une constante enum, ObjectOutputStream écrit la valeur renvoyée par la méthode name de la constante enum. Pour désérialiser une constante enum, ObjectInputStream lit le nom de la constante à partir du flux; la constante désérialisée est alors obtenue en appelant la méthode java.lang.Enum.valueOf , en passant le type enum de la constante avec le nom de la constante reçue comme arguments. Comme les autres objets sérialisables ou extériorisables, les constantes enum peuvent fonctionner comme les cibles des références arrières apparaissant ultérieurement dans le flux de sérialisation.

Le processus par lequel les constantes enum sont sérialisées ne peut pas être personnalisé: les writeObject , readObject , readObjectNoData , writeReplace et readResolve spécifiques à la classe définies par les types enum sont ignorées pendant la sérialisation et la désérialisation. De même, toutes les déclarations de champ serialPersistentFields ou serialVersionUID sont également ignorées - tous les types enum ont un serialVersionUID fixe de 0L . La documentation des champs sérialisables et des données pour les types enum est inutile, car il n'y a pas de variation dans le type de données envoyées.

Cité à partir de documents Oracle

Un autre problème avec les Singletons classiques est qu'une fois que vous avez implémenté l'interface Serializable , ils ne restent plus Singleton car la méthode readObject() renvoie toujours une nouvelle instance comme le constructeur en Java. Cela peut être évité en utilisant readResolve() et en readResolve() instance nouvellement créée en remplaçant par singleton comme ci-dessous

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Cela peut devenir encore plus complexe si votre classe Singleton maintient l'état, car vous devez les rendre transitoires, mais avec Enum Singleton, la sérialisation est garantie par JVM.

Bonne lecture

  1. Modèle Singleton
  2. Enums, singletons et désérialisation
  3. Verrouillage à double vérification et le motif Singleton

La solution postée par Stu Thompson est valide dans Java5.0 et plus tard. Mais je préférerais ne pas l'utiliser parce que je pense que c'est une erreur.

Il est facile d'oublier la déclaration volatile et difficile à comprendre pourquoi il est nécessaire. Sans le volatile, ce code ne serait plus sûr pour les threads en raison de l'antipatinage de verrouillage à double vérification. Voir plus à ce sujet au paragraphe 16.2.4 de Java Concurrency in Practice . En bref: Ce modèle (antérieur à Java5.0 ou sans l'instruction volatile) peut renvoyer une référence à l'objet Bar qui est (encore) dans un état incorrect.

Ce modèle a été inventé pour l'optimisation des performances. Mais ce n'est vraiment plus une réelle préoccupation. Le code d'initialisation paresseux suivant est rapide et, plus important, plus facile à lire.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

N'oubliez pas que Singleton n'est qu'un Singleton pour le Classloader qui l'a chargé. Si vous utilisez plusieurs chargeurs (conteneurs), chacun pourrait avoir sa propre version du singleton.


Selon l'usage, il y a plusieurs réponses "correctes".

Depuis java5, la meilleure façon de le faire est d'utiliser une énumération:

public enum Foo {
   INSTANCE;
}

Pré java5, le cas le plus simple est:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Passons en revue le code. D'abord, vous voulez que la classe soit finale. Dans ce cas, j'ai utilisé le mot-clé final pour informer les utilisateurs qu'il est définitif. Ensuite, vous devez rendre le constructeur privé pour empêcher les utilisateurs de créer leur propre Foo. Le lancement d'une exception à partir du constructeur empêche les utilisateurs d'utiliser la réflexion pour créer un deuxième Foo. Ensuite, vous créez un private static final Foo pour contenir la seule instance, et une méthode public static Foo getInstance() pour le renvoyer. La spécification Java vérifie que le constructeur n'est appelé que lorsque la classe est utilisée pour la première fois.

Lorsque vous avez un objet très volumineux ou un code de construction lourd ET que vous avez également d'autres méthodes ou champs statiques accessibles qui pourraient être utilisés avant qu'une instance soit nécessaire, alors et seulement alors vous devez utiliser l'initialisation paresseuse.

Vous pouvez utiliser une private static class pour charger l'instance. Le code ressemblerait alors à:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Depuis la ligne private static final Foo INSTANCE = new Foo(); est seulement exécutée quand la classe FooLoader est réellement utilisée, ceci prend soin de l'instantiation paresseuse, et est-il garanti sûr de thread.

Lorsque vous voulez également pouvoir sérialiser votre objet, vous devez vous assurer que la désérialisation ne créera pas de copie.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

La méthode readResolve() s'assurera que la seule instance sera renvoyée, même si l'objet a été sérialisé lors d'une exécution précédente de votre programme.


Si vous n'avez pas besoin de chargement paresseux, essayez simplement

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Si vous voulez un chargement paresseux et que vous voulez que votre Singleton soit thread-safe, essayez le motif de double vérification

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

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

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Comme le modèle de double vérification n'est pas garanti (en raison d'un problème avec les compilateurs, je ne sais rien de plus), vous pouvez aussi essayer de synchroniser toute la méthode getInstance ou créer un registre pour tous vos singletons.


Voici 3 approches différentes

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Double coché Verrouillage / chargement paresseux

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Méthode d'usine statique

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

Vous avez besoin double-checking idiome si vous avez besoin de charger la variable d'instance d'une classe paresseusement. Si vous avez besoin de charger une variable statique ou un singleton paresseusement, vous avez besoin d'une initilisation à la demande .

De plus, si le singleton doit être sériable, tous les autres champs doivent être transitoires et la méthode readResolve () doit être implémentée afin de maintenir l'invariant de l'objet singleton. Sinon, chaque fois que l'objet est désérialisé, une nouvelle instance de l'objet sera créée. Ce que readResolve () fait est de remplacer le nouvel objet lu par readObject (), ce qui a forcé ce nouvel objet à être récupéré car il n'y a pas de variable qui s'y réfère.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 

Wikipedia a quelques examples de singletons, également en Java. L'implémentation de Java 5 semble assez complète et est sûre pour les threads (verrouillage à double contrôle appliqué).


Disclaimer: Je viens de résumer toutes les réponses impressionnantes et l'ai écrit dans mes mots.

En mettant en œuvre Singleton, nous avons 2 options
1. Chargement paresseux
2. Chargement anticipé

Le chargement paresseux ajoute un surcoût (beaucoup pour être honnête) donc utilisez-le seulement quand vous avez un très gros objet ou un gros code de construction ET avez aussi d'autres méthodes ou champs statiques accessibles qui pourraient être utilisés avant qu'une instance soit nécessaire vous devez utiliser l'initialisation paresseux. Sinon, choisir un chargement anticipé est un bon choix.

Le moyen le plus simple d'implémenter Singleton est

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Tout est bon sauf son singleton chargé tôt. Essayons singleton paresseux chargé

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Jusqu'ici tout va bien mais notre héros ne survivra pas en se battant seul avec de multiples fils diaboliques qui veulent beaucoup de nombreuses instances de notre héros. Alors, protégeons-le du multi-threads maléfique

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

mais ce n'est pas suffisant pour protéger le héros, vraiment !!! C'est le meilleur que nous pouvons / devrions faire pour aider notre héros

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

C'est ce qu'on appelle "l'idiome de verrouillage à double vérification". Il est facile d'oublier la déclaration volatile et difficile à comprendre pourquoi il est nécessaire.
Pour plus de détails: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Maintenant, nous sommes sûrs du fil du mal, mais qu'en est-il de la sérialisation cruelle? Nous devons nous assurer que même si de-serialiaztion aucun nouvel objet n'est créé

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

La méthode readResolve() s'assurera que la seule instance sera renvoyée, même si l'objet a été sérialisé lors d'une exécution précédente de notre programme.

Enfin, nous avons ajouté suffisamment de protection contre les threads et la sérialisation, mais notre code semble volumineux et moche. Permet à notre héros une marque

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Oui, c'est notre même héros :)
Depuis la ligne private static final Foo INSTANCE = new Foo(); est seulement exécuté quand la classe FooLoader est réellement utilisée, ceci s'occupe de l'instanciation paresseuse,

et est-il garanti d'être thread safe.

Et nous sommes venus si loin, voici la meilleure façon de réaliser tout ce que nous avons fait est le meilleur moyen possible

 public enum Foo {
       INSTANCE;
   }

Qui sera traité en interne comme

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Ce n'est plus la peur de la sérialisation, des threads et du code moche. Les singulets ENUMS sont également initialisés paresseusement .

Cette approche est fonctionnellement équivalente à l'approche de terrain public, sauf qu'elle est plus concise, fournit la machine de sérialisation gratuitement, et fournit une garantie absolue contre l'instanciation multiple, même face à des attaques sophistiquées de sérialisation ou de réflexion. Bien que cette approche n'ait pas encore été largement adoptée, un type enum à un élément est le meilleur moyen de mettre en œuvre un singleton.

-Joshua Bloch dans "Java efficace"

Maintenant, vous avez peut-être réalisé pourquoi ENUMS est considéré comme le meilleur moyen de mettre en œuvre Singleton et merci pour votre patience :)
Mise à jour sur mon blog .


Version 1:

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

Chargement paresseux, thread sécurisé avec blocage, faible performance en raison de synchronized .

Version 2:

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

Chargement paresseux, thread sûr avec non-bloquant, haute performance.


Another argument often used against Singletons are their testability problems. Singletons are not easily mockable for testing purposes. If this turns out to be a problem, I like to make the following slight modification:

public class SingletonImpl {

    private static SingletonImpl instance;

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

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

The added setInstance method allows setting a mockup implementation of the singleton class during testing:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

This also works with early initialization approaches:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

This has the drawback of exposing this functionality to the normal application too. Other developers working on that code could be tempted to use the ´setInstance´ method to alter alter a specific function and thus changing the whole application behaviour, therefore this method should contain at least a good warning in it's javadoc.

Still, for the possibility of mockup-testing (when needed), this code exposure may be an acceptable price to pay.


Have a look at this post.

Exemples de motifs de conception GoF dans les bibliothèques principales de Java

From the best answer's "Singleton" section,

Singleton (recognizeable by creational methods returning the same instance (usually of itself) everytime)

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

You can also learn the example of Singleton from Java native classes themselves.


I still think after java 1.5, enum is the best available singleton implementation available as it also ensures that even in the multi threaded environments - only one instance is created.

public enum Singleton{ INSTANCE; }

and you are done !!!


Sometimes a simple " static Foo foo = new Foo(); " is not enough. Just think of some basic data insertion you want to do.

On the other hand you would have to synchronize any method that instantiates the singleton variable as such. Synchronisation is not bad as such, but it can lead to performance issues or locking (in very very rare situations using this example. The solution is

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Now what happens? The class is loaded via the class loader. Directly after the class was interpreted from a byte Array, the VM executes the static { } - block. that's the whole secret: The static-block is only called once, the time the given class (name) of the given package is loaded by this one class loader.


This is how to implement a simple singleton :

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){};
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

This is how to properly lazy create your singleton :

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){};
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

simplest singleton class

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}






design-patterns