Quel est un moyen efficace d'implémenter un modèle singleton en Java? [fermé]


Quel est un moyen efficace d'implémenter un modèle singleton en Java?

Author: Erick Robertson, 2008-09-16

30 answers

Utiliser une énumération:

public enum Foo {
    INSTANCE;
}

Joshua Bloch a expliqué cette approche dans sa conférenceEffective Java Reloaded à Google I/O 2008: lien vers la vidéo . Voir aussi les diapositives 30-32 de sa présentation (effective_java_reloaded.pdf):

La bonne façon d'implémenter un Singleton Sérialisable

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Edit: Un partie en ligne de "Effective Java" dit:

" Cette approche est fonctionnellement équivalente au public l'approche sur le terrain, sauf qu'elle est plus concise, fournit la machine de sérialisation gratuitement et fournit une garantie à toute épreuve 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 d'énumération à un seul élément est le meilleur moyen d'implémenter un singleton."

 726
Author: Stephen Denne, 2015-05-15 09:23:02

Selon l'utilisation, il existe plusieurs réponses "correctes".

Depuis java5 la meilleure façon de le faire est d'utiliser un enum:

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 faire savoir aux utilisateurs qu'il est définitif. Ensuite, vous devez rendre le constructeur privé pour empêcher les utilisateurs de créer leur propre Foo. Lancer une exception du constructeur empêche les utilisateurs pour utiliser la réflexion pour créer un deuxième Foo. Ensuite, vous créez un champ private static final Foo pour contenir la seule instance et une méthode public static Foo getInstance() pour la renvoyer. La spécification Java garantit 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 peuvent être utilisés avant qu'une instance ne soit nécessaire, alors et seulement alors vous devez utiliser l'initialisation paresseuse.

Vous pouvez utiliser un 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;
    }
}

Étant donné que la ligne private static final Foo INSTANCE = new Foo(); n'est exécutée que lorsque la classe FooLoader est réellement utilisée, cela prend soin de l'instanciation paresseuse et est-il garanti pour être thread safe.

Lorsque vous souhaitez é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 {[9] } s'assurera que la seule instance sera renvoyée, même lorsque l'objet a été sérialisé lors d'une exécution précédente de votre programme.

 219
Author: Roel Spilker, 2016-12-05 08:39:13

Avertissement: Je viens de résumer toutes les réponses impressionnantes et de l'écrire dans mes mots.


Lors de l'implémentation de Singleton, nous avons 2 options
1. Chargement paresseux
2. Chargement anticipé

Le chargement paresseux ajoute une surcharge de bits(beaucoup pour être honnête), utilisez-la uniquement 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 ne soit nécessaire, alors et seulement alors vous devez utilisez l'initialisation paresseuse.Sinon, choisir un chargement précoce 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'à présent si bon, mais notre héros ne survivra pas en se battant seul avec plusieurs fils maléfiques qui veulent beaucoup beaucoup d'instance de notre héros. Permet donc de le protéger du mal multi threading

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 il ne suffit pas de protéger héros, Vraiment!!! C'est le mieux 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;
    }
}

Ceci est appelé "idiome de verrouillage à double vérification". Il est facile d'oublier la déclaration volatile et difficile de comprendre pourquoi c'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 maléfique, mais qu'en est-il de la sérialisation cruelle? Nous devons nous assurer que même pendant la dé-sérialiaztion 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 lorsque 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 a l'air volumineux et laid. Permet de donner à notre héros une marque sur

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:)
Étant donné que la ligne private static final Foo INSTANCE = new Foo(); n'est exécutée que lorsque la classe FooLoader est réellement utilisée, cela prend soin du paresseux instanciation,

Et est-il garanti pour être thread safe.

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

 public enum Foo {
       INSTANCE;
   }

Qui en interne sera traité comme

public class Foo {

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

C'est tout plus peur de la sérialisation, des threads et du code laid. Aussi Les ENUMS singleton sont initialisés paresseusement.

Cette approche est fonctionnellement équivalente à l'approche du domaine public, sauf qu'il est plus concis, fournit les machines de sérialisation pour libre, et fournit une garantie à toute épreuve contre plusieurs instanciation, même face à une sérialisation sophistiquée ou attaques de réflexion. Bien que cette approche n'ait pas encore été largement adoptée, un type enum à un seul élément est le meilleur moyen d'implémenter un singleton.

-Joshua Bloch dans "Effective Java"

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

 121
Author: xyz, 2017-05-23 10:31:37

La solution publiée par Stu Thompson est valide dans Java5.0 et versions ultérieures. Mais je préférerais ne pas l'utiliser parce que je pense que c'est sujet aux erreurs.

Il est facile d'oublier la déclaration volatile et difficile de comprendre pourquoi elle est nécessaire. Sans le volatile, ce code ne serait plus thread safe en raison de l'antipattern de verrouillage à double vérification. Voir plus à ce sujet dans le paragraphe 16.2.4 de Java Concurrency in Practice. En bref: Ce modèle (avant Java5.0 ou sans le volatile statement) pourrait renvoyer une référence à l'objet Bar qui est (toujours) dans un état incorrect.

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

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

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
 120
Author: Benno Richters, 2012-09-06 16:40:30

Thread safe dans Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT : Faites attention au modificateur volatile ici. :) C'est important car sans cela, les autres threads ne sont pas garantis par le JMM (Java Memory Model) pour voir les modifications de sa valeur. La synchronisation ne prend pas en charge - elle sérialise uniquement l'accès à ce bloc de code.

MODIFIER 2 : La réponse de @ Bno détaille l'approche recommandée par Bill Pugh (FindBugs) et est mieux défendable. Allez lire et voter sa réponse trop.

 93
Author: Stu Thompson, 2014-05-14 13:48:09

Oubliez initialisation, c'est trop problématique. C'est la solution la plus simple:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
 87
Author: Jonathan, 2015-04-20 15:01:50

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 mal intrinsèquement à cela, je suppose, mais c'est juste un mécanisme pour exposer certaines ressources/données globales, alors assurez-vous que c'est le meilleur moyen. En particulier, j'ai trouvé l'injection de dépendance plus utile, en particulier si vous utilisez également des tests unitaires, car DI vous permet d'utiliser des ressources moquées à des fins de test.

 45
Author: Neil Burroughs, 2014-09-11 08:27:32

N'oubliez pas que le 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.

 25
Author: Javamann, 2008-09-16 18:12:31

Je suis mystifié par certaines des réponses qui suggèrent DI comme alternative à l'utilisation de singletons; ce sont des concepts sans rapport. Vous pouvez utiliser DI pour injecter des instances singleton ou non singleton (par exemple par thread). Au moins, cela est vrai si vous utilisez Spring 2.x, je ne peux pas parler pour d'autres cadres DI.

Donc ma réponse à l'OP serait (dans tous les exemples de code sauf le plus trivial) à:

  1. Utilisez un framework DI comme Spring, puis
  2. Faites-le partie de votre configuration DI que vos dépendances soient des singletons, une portée de requête, une portée de session ou autre.

Cette approche vous donne une belle architecture découplée (et donc flexible et testable) où l'utilisation d'un singleton est un détail d'implémentation facilement réversible (à condition que les singletons que vous utilisez soient threadsafe, bien sûr).

 20
Author: Andrew Swan, 2008-09-16 12:06:04

Considérez vraiment pourquoi vous avez besoin d'un singleton avant de l'écrire. Il y a un débat quasi religieux sur leur utilisation que vous pouvez facilement trébucher si vous google singletons en Java.

Personnellement, j'essaie d'éviter les singletons aussi souvent que possible pour de nombreuses raisons, encore une fois la plupart peuvent être trouvées en googlant les singletons. Je pense que très souvent les singletons sont abusés parce qu'ils sont faciles à comprendre par tout le monde, ils sont utilisés comme un mécanisme pour obtenir des données" globales " dans un OO conception et ils sont utilisés parce qu'il est facile de contourner la gestion du cycle de vie des objets (ou vraiment penser à la façon dont vous pouvez faire A de l'intérieur B). Regardez des choses comme l'inversion de contrôle (IoC) ou l'injection de dépendance (DI) pour un beau milieu.

Si vous en avez vraiment besoin, wikipedia a un bon exemple d'implémentation correcte d'un singleton.

 19
Author: , 2008-09-16 09:48:54

Voici 3 approches différentes

1) Enum

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

2) Verrouillage à double vérification / 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 statique d'usine

/**
* 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;
    }
}
 15
Author: Abhijit Gaikwad, 2013-02-17 03:46:40

J'utilise le framework Spring pour gérer mes singletons. Il n'applique pas le "singleton-ness" de la classe (ce que vous ne pouvez pas vraiment faire de toute façon s'il y a plusieurs chargeurs de classe impliqués) mais fournit un moyen très facile de construire et de configurer différentes usines pour créer différents types d'objets.

 12
Author: Matt, 2008-09-22 20:44:55

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ûr 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 safe avec non bloquant, haute performance.

 11
Author: coderz, 2015-06-09 04:08:16

Wikipedia a certains exemples de singletons, également en Java. L'implémentation Java 5 semble assez complète et est thread-safe (verrouillage à double vérification appliqué).

 10
Author: macbirdie, 2008-09-16 09:35:42

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 modèle 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 pour fonctionner (en raison d'un problème avec les compilateurs, je n'en sais rien de plus à ce sujet.), vous pouvez également essayer de synchroniser l'ensemble de la méthode getInstance ou créer un registre pour tous vos Singletons.

 10
Author: Aleksi Yrttiaho, 2008-09-17 04:36:50

Je dirais Enum singleton

Singleton utiliser 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 pour assurer la sécurité des threads de cette méthode, si elle 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 concernant les objets sérialisables, qui doivent être contournés dans les autres modes.

/**
* 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 facile que d'appeler la méthode getInstance() sur Singleton.

1.12 Sérialisation des constantes Enum

Les constantes Enum sont sérialisées différemment des objets sérialisables ou externalisables ordinaires. La forme sérialisée d'une constante enum se compose uniquement de son nom; les valeurs de champ de la constante ne sont pas présentes dans le formulaire. Pour sérialiser une constante d'énumération, ObjectOutputStream écrit la valeur renvoyée par l'énumération méthode de nom de constante. Pour désérialiser une constante enum, ObjectInputStream lit le nom de la constante du flux; la constante désérialisée est ensuite 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 d'autres objets sérialisables ou externalisables, les constantes enum peuvent fonctionner comme cibles de références antérieures 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é: toute classe spécifique writeObject, readObject, readObjectNoData, writeReplace, et readResolve les méthodes 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 d'énumération 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.

Je ne sais pas si c'est le cas.]}

Un autre problème avec les Singletons conventionnels, une fois que vous implémentez l'interface Serializable, ils ne restent plus Singleton car la méthode readObject() renvoie toujours une nouvelle instance comme constructor en Java. Cela peut être évité en utilisant readResolve() et en rejetant l'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. Enum, Singletons et désérialisation
  3. Verrouillage à double vérification et le modèle Singleton
 8
Author: NullPoiиteя, 2017-05-23 11:55:02
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");
    }
}
 6
Author: Dheeraj Sachan, 2017-03-09 13:25:25

Peut être un peu en retard dans le jeu à ce sujet, mais il y a beaucoup de nuances autour de la mise en œuvre d'un singleton. Le modèle de support ne peut pas être utilisé dans de nombreuses situations. Et IMO lors de l'utilisation d'un volatile - vous devez également utiliser une variable locale. Commençons par le début et itérons sur le problème. Vous verrez ce que je veux dire.


La première tentative pourrait ressembler à ceci:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Ici, nous avons la classe MySingleton qui a un membre statique privé appelé INSTANCE, et un méthode statique publique appelée getInstance (). La première fois que getInstance() est appelée, le membre de l'INSTANCE est null. Le flux tombera alors dans la condition de création et créera une nouvelle instance de la classe MySingleton. Les appels ultérieurs à getInstance () constateront que la variable d'INSTANCE est déjà définie et ne créeront donc pas une autre instance MySingleton. Cela garantit qu'il n'y a qu'une seule instance de MySingleton qui est partagée entre tous les appelants de getInstance().

Mais ceci la mise en œuvre a un problème. Les applications multithread auront une condition de concurrence lors de la création de l'instance unique. Si plusieurs threads d'exécution frappent la méthode getInstance () en même temps (ou autour), ils verront chacun le membre de l'INSTANCE comme null. Cela entraînera la création par chaque thread d'une nouvelle instance MySingleton et la définition ultérieure du membre de l'INSTANCE.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Ici, nous avons utilisé le mot clé synchronized dans la signature de la méthode pour synchroniser Méthode getInstance (). Cela corrigera certainement notre condition de concurrence. Les threads vont maintenant bloquer et entrer la méthode un à la fois. Mais cela crée également un problème de performance. Non seulement cette implémentation synchronise la création de l'instance unique, mais elle synchronise tous les appels à getInstance(), y compris les lectures. Les lectures n'ont pas besoin d'être synchronisées car elles renvoient simplement la valeur de l'INSTANCE. Puisque les lectures constitueront la majeure partie de nos appels (rappelez-vous, l'instanciation ne se produit que lors du premier appel), nous subirons un coup de performance inutile en synchronisant toute la méthode.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Ici, nous avons déplacé la synchronisation de la signature de la méthode vers un bloc synchronisé qui enveloppe la création de l'instance MySingleton. Mais cela résout-il notre problème? Eh bien, nous ne bloquons plus les lectures, mais nous avons également fait un pas en arrière. Plusieurs threads frapperont la méthode getInstance () au même moment ou à peu près et ils verront tous le membre de l'INSTANCE comme null. Ils seront alors appuyez sur le bloc synchronisé où l'on obtiendra le verrou et créera l'instance. Lorsque ce thread quitte le bloc, les autres threads se disputeront le verrou, et un par un chaque thread tombera à travers le bloc et créera une nouvelle instance de notre classe. Nous sommes donc de retour là où nous avons commencé.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Ici, nous émettons une autre vérification de L'INTÉRIEUR du bloc. Si le membre de l'INSTANCE a déjà été défini, nous sauterons l'initialisation. C'est ce qu'on appelle le verrouillage à double vérification.

Ceci résout notre problème de plusieurs instanciation. Mais encore une fois, notre solution a présenté un autre défi. D'autres threads peuvent ne pas "voir" que le membre de l'INSTANCE a été mis à jour. Cela est dû à la façon dont Java optimise les opérations de mémoire. Les threads copient les valeurs d'origine des variables de la mémoire principale dans le cache du PROCESSEUR. Les modifications apportées aux valeurs sont ensuite écrites et lues dans ce cache. Il s'agit d'une fonctionnalité de Java conçue pour optimiser les performances. Mais cela crée un problème pour notre singleton application. Un deuxième thread - en cours de traitement par un processeur ou un cœur différent, en utilisant un cache différent-ne verra pas les modifications apportées par le premier. Cela entraînera le deuxième thread à voir le membre de l'INSTANCE comme null forçant la création d'une nouvelle instance de notre singleton.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Nous résolvons cela en utilisant le mot clé volatile sur la déclaration du membre de l'INSTANCE. Cela indiquera au compilateur de toujours lire et écrire dans la mémoire principale, et non dans le cache du PROCESSEUR.

Mais ce simple changement a un coût. Parce que nous contournons le cache CPU, nous allons prendre un coup de performance chaque fois que nous opérons sur le membre D'INSTANCE volatile - ce que nous faisons 4 fois. Nous vérifions l'existence (1 et 2), définissons la valeur (3), puis retournons la valeur (4). On pourrait soutenir que ce chemin est le cas fringe car nous ne créons l'instance que lors du premier appel de la méthode. Peut-être qu'un coup de performance sur la création est tolérable. Mais même notre cas d'utilisation principal, reads, fonctionnera sur le membre volatile deux fois. Une fois pour vérifier l'existence, et encore pour retourner sa valeur.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

Puisque l'atteinte des performances est due au fonctionnement directement sur le membre volatile, définissons une variable locale à la valeur du volatile et opérons sur la variable locale à la place. Cela réduira le nombre de fois que nous opérons sur le volatile, récupérant ainsi une partie de nos performances perdues. Notez que nous devons définir à nouveau notre variable locale lorsque nous entrons dans le bloc synchronisé. Cela garantit il est à jour avec tous les changements survenus pendant que nous attendions la serrure.

J'ai écrit un article à ce sujet récemment. Déconstruire Le Singleton. Vous pouvez trouver plus d'informations sur ces exemples et un exemple de "titulaire" modèle là. Il existe également un exemple réel présentant l'approche volatile à double vérification. Espérons que cette aide.

 4
Author: Michael Andrews, 2017-07-12 16:06:06

Vous avez besoin d' double vérification idiome si vous devez 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 de idiom d'initialisation à la demande.

De plus, si le singleton doit être seriliazble, 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éé. 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.
} 
 3
Author: Onur, 2011-08-02 20:49:18

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

  1. Selon Joshua Bloch-Enum serait le meilleur.

  2. Vous pouvez également utiliser le verrouillage à double contrôle.

  3. Même la classe statique interne peut être utilisée.

 3
Author: Shailendra Singh, 2015-08-29 12:42:30

Enum singleton

Le moyen le plus simple d'implémenter un Singleton sans thread est d'utiliser une énumération

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

Ce code fonctionne depuis l'introduction d'Enum dans Java 1.5

Verrouillage vérifié deux fois

Si vous voulez coder un singleton "classique" qui fonctionne 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 car l'implémentation du mot clé volatile était différent.

Chargement précoce 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");
  }

}
 3
Author: Dan Moldovan, 2016-05-11 17:58:05

Voici comment implémenter un 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;
    }
}

Voici comment créer correctement votre 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();
    }
}
 3
Author: Nicolas Filotto, 2016-05-29 18:50:11

Pour JSE 5.0 et supérieur, utilisez l'approche Enum, sinon utilisez l'approche statique singleton holder ( (une approche de chargement paresseux décrite par Bill Pugh). Cette dernière solution est également thread-safe sans nécessiter de constructions de langage spéciales (c'est-à-dire volatile ou synchronisé).

 2
Author: raoadnan, 2013-05-22 18:51:33

Un autre argument souvent utilisé contre les Singletons sont leurs problèmes de testabilité. Les singletons ne sont pas facilement moquables à des fins de test. Si cela s'avère être un problème, j'aime apporter la légère modification suivante:

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");
    }
}

La méthode ajoutée setInstance permet de définir une implémentation maquette de la classe singleton lors des tests:

public class SingletonMock extends SingletonImpl {

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

}

Cela fonctionne également avec les approches d'initialisation précoce:

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");
    }

}

Ceci a l'inconvénient d'exposer cette fonctionnalité au application normale aussi. D'autres développeurs travaillant sur ce code pourraient être tentés d'utiliser la méthode setInstance pour modifier une fonction spécifique et ainsi changer le comportement de l'application entière, donc cette méthode devrait contenir au moins un bon avertissement dans son javadoc.

Néanmoins, pour la possibilité de tester des maquettes (au besoin), cette exposition au code peut être un prix acceptable à payer.

 2
Author: user3792852, 2016-08-14 12:08:22

Classe singleton la plus simple

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
 0
Author: rohan kamat, 2014-05-14 13:44:14

Je pense toujours qu'après java 1.5, enum est la meilleure implémentation singleton disponible car elle garantit également que même dans les environnements multithread - une seule instance est créée.

public enum Singleton{ INSTANCE; }

Et vous avez terminé !!!

 0
Author: shikjohari, 2015-01-06 07:22:59

Jetez un oeil à ce post.

Exemples de modèles de conception GoF dans les bibliothèques de base de Java

De la section "Singleton" de la meilleure réponse,

Singleton (reconnaissable par des méthodes de création renvoyant la même instance (généralement d'elle-même) à chaque fois)

  • java.lang.Runtime # getRuntime ()
  • java.awt.Bureau#getDesktop()
  • java.lang.Le système # getSecurityManager()

Vous pouvez également apprendre le exemple de Singleton à partir des classes natives Java elles-mêmes.

 0
Author: phi, 2017-05-23 12:18:27

Le meilleur modèle singleton que j'ai jamais vu utilise l'interface Fournisseur.

  • Il est générique et réutilisable
  • Il prend en charge l'initialisation paresseuse
  • Il n'est synchronisé que jusqu'à ce qu'il ait été initialisé, puis le fournisseur bloquant est remplacé par un fournisseur non bloquant.

Voir ci-dessous:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
 0
Author: Robert Thornton, 2018-09-10 21:05:02

Parfois, un simple "static Foo foo = new Foo();" n'est pas assez. Pensez simplement à une insertion de données de base que vous voulez faire.

D'autre part, vous devrez synchroniser toute méthode qui instancie la variable singleton en tant que telle. La synchronisation n'est pas mauvaise en tant que telle, mais elle peut entraîner des problèmes de performances ou de verrouillage (dans de très rares situations en utilisant cet exemple. La solution est

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;
    }

}

Maintenant, que se passe-t-il? La classe est chargée par le chargeur de classes. Directement après la classe était interprétée à partir d'un Tableau d'octets, la machine virtuelle exécute la static { } - bloc. c'est tout le secret: Le bloc statique n'est appelé qu'une seule fois, le temps que la classe donnée (nom) du paquet donné soit chargée par ce chargeur de classe unique.

 -3
Author: Georgi, 2013-04-26 06:03:05
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
    if (INSTANCE != null)
        throw new IllegalStateException (“Already instantiated...”);
}

    public synchronized static Singleton getInstance() { 
    return INSTANCE;

    }

}

Comme nous avons ajouté le mot-clé Synchronisé avant getInstance, nous avons évité la condition de concurrence dans le cas où deux threads appellent getInstance en même temps.

 -5
Author: somenath mukhopadhyay, 2014-05-14 13:44:05