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


812

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


1
"Quelle est une manière efficace d'implémenter un modèle singleton en Java?" veuillez définir efficace.
Marian Paździoch

medium.com/@kevalpatel2106/… . Ceci est l'article complet sur la façon d'assurer la sécurité des threads, de la réflexion et de la sérialisation dans le modèle singleton. C'est la bonne source pour comprendre les avantages et les limites de la classe singleton.
Keval Patel

Comme le souligne Joshua Bloch dans Effective Java, enum singleton est la meilleure solution. Ici, j'ai classé les différentes implémentations comme paresseuses / avides, etc.
isaolmez

Réponses:


782

Utilisez une énumération:

public enum Foo {
    INSTANCE;
}

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

La bonne façon de mettre en œuvre 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: Une partie en ligne de "Java efficace" dit:

"Cette approche est fonctionnellement équivalente à l'approche du domaine public, sauf qu'elle est plus concise, fournit gratuitement le mécanisme de sérialisation et offre une garantie à toute épreuve contre les instanciations multiples, même face à des attaques sophistiquées de sérialisation ou de réflexion. Bien que cette approche ait encore à être largement adopté, un type d'énumération à un seul élément est le meilleur moyen d'implémenter un singleton . "


203
Je pense que les gens devraient commencer à considérer les énumérations comme une simple classe avec une fonctionnalité. si vous pouvez répertorier les instances de votre classe au moment de la compilation, utilisez une énumération.
Amir Arad

7
Personnellement, je ne trouve pas souvent la nécessité d'utiliser directement le motif singleton. J'utilise parfois l'injection de dépendances de Spring avec un contexte d'application qui contient ce qu'il appelle des singletons. Mes classes utilitaires ont tendance à ne contenir que des méthodes statiques, et je n'en ai pas besoin.
Stephen Denne

3
Salut, Quelqu'un peut-il me dire comment ce type de singleton peut être moqué et testé dans des cas de test. J'ai essayé d'échanger une fausse instance de singleton pour ce type mais je n'ai pas pu.
Ashish Sharma

29
Je suppose que cela a du sens, mais je ne l'aime toujours pas. Comment pourriez-vous créer un singleton qui étend une autre classe? Si vous utilisez une énumération, vous ne pouvez pas.
chharvey

11
@bvdb: Si vous voulez beaucoup de flexibilité, vous avez déjà foiré en implémentant un singleton en premier lieu. La possibilité de créer une instance indépendante lorsque vous en avez besoin est plutôt inestimable en soi.
cHao

233

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

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

public enum Foo {
   INSTANCE;
}

Avant 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. Tout d'abord, vous voulez que la classe soit finale. Dans ce cas, j'ai utilisé le finalmot clé 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. Le fait de lever une exception 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 Foochamp pour contenir la seule instance et une public static Foo getInstance()méthode pour la renvoyer. La spécification Java garantit que le constructeur n'est appelé que lors de la première utilisation de la classe.

Lorsque vous avez un très grand objet 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 utiliser l'initialisation paresseuse.

Vous pouvez utiliser un private static classpour 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 garantie d'ê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 readResolve()s'assurera que la seule instance sera renvoyée, même lorsque l'objet a été sérialisé lors d'une précédente exécution de votre programme.


32
Le contrôle de la réflexion est inutile. Si un autre code utilise la réflexion sur les privés, c'est Game Over. Il n'y a aucune raison d'essayer même de fonctionner correctement sous une telle utilisation abusive. Et si vous essayez, ce sera de toute façon une "protection" incomplète, juste beaucoup de code gaspillé.
Wouter Coekaerts le

5
> "D'abord, vous voulez que la classe soit finale". Quelqu'un pourrait-il développer cela s'il vous plaît?
PlagueHammer

2
La protection contre la désérialisation est complètement rompue (je pense que cela est mentionné dans Effective Java 2nd Ed).
Tom Hawtin - tackline

9
-1 ce n'est absolument pas le cas le plus simple, il est artificiel et inutilement complexe. Regardez la réponse de Jonathan pour la solution la plus simple qui soit suffisante dans 99,9% des cas.
Michael Borgwardt

2
Ceci est utile lorsque votre singleton doit hériter d'une superclasse. Vous ne pouvez pas utiliser le modèle singleton enum dans ce cas, car les énumérations ne peuvent pas avoir de superclasse (elles peuvent cependant implémenter des interfaces). Par exemple, Google Guava utilise un champ final statique lorsque le modèle enum singleton n'est pas une option: code.google.com/p/guava-libraries/source/browse/trunk/guava/src/…
Etienne Neveu

139

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


Lors de la mise en œuvre de Singleton, nous avons 2 options
1. Chargement paresseux
2. Chargement précoce

Le chargement différé ajoute un peu de surcharge (pour être honnête), utilisez-le 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 utiliser l'initialisation paresseuse. Sinon, choisir un chargement précoce est un bon choix.

Le moyen le plus simple de mettre en œuvre 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 va bien sauf son singleton chargé tôt. Permet d'essayer un singleton paresseux

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 plusieurs fils diaboliques qui veulent de nombreuses instances de notre héros. Permet donc de le protéger du diabolique 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 le 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 elle est nécessaire.
Pour plus de détails: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Maintenant, nous sommes sûrs du mauvais fil, mais qu'en est-il de la sérieuse sérialisation? Nous devons nous assurer que même pendant la dé-sérialisation 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 retournée, même lorsque l'objet a été sérialisé lors d'une précédente exécution de notre programme.

Enfin, nous avons ajouté suffisamment de protection contre les threads et la sérialisation, mais notre code semble volumineux et laid. Laissons notre héros se refaire une beauté

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 :)
Puisque la ligne private static final Foo INSTANCE = new Foo();n'est exécutée que lorsque la classeFooLoader est réellement utilisée, cela prend soin de l'instanciation paresseuse,

et est-il garanti ê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 ça! Plus de peur de la sérialisation, des threads et du code laid. Les singleton ENUMS sont également initialisés paresseusement .

Cette approche est fonctionnellement équivalente à l'approche du domaine public, sauf qu'elle est plus concise, fournit gratuitement le mécanisme de sérialisation et offre une garantie à toute épreuve contre les instanciations multiples, 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 à élément unique est le meilleur moyen d'implémenter un singleton.

-Joshua Bloch dans "Java efficace"

Maintenant, vous avez peut-être compris pourquoi ENUMS est considéré comme le meilleur moyen d'implémenter Singleton et merci pour votre patience :)
Mis à jour sur mon blog .


3
Juste une précision: les singletons implémentés en utilisant enum sont initialisés paresseusement. Détails ici: stackoverflow.com/questions/16771373/…

2
très bonne réponse. une dernière chose, remplacer la méthode clone pour lever l'exception.
riship89

2
@xyz belles explications, j'ai vraiment apprécié et appris très facilement et j'espère ne jamais avoir oublié cela
Premraj

1
L'une des meilleures réponses que j'ai jamais eues sur stackoverflow. Merci!
Shay Tsadok

1
Il y a un problème de sérialisation avec l'utilisation des énumérations comme singleton: les valeurs des champs membres ne sont pas sérialisées et ne sont donc pas restaurées. Voir Spécification de sérialisation d'objet Java, version 6.0 . Autre problème: pas de versioning - tous les types d'énumération ont un fixe serialVersionUIDde 0L. Troisième problème: pas de personnalisation: les méthodes 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.
Basil Bourque

124

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 car je pense qu'il 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 sûr pour les threads en raison de l'anti-modèle de verrouillage à double vérification. Voir plus à ce sujet au paragraphe 16.2.4 de la concurrence Java en pratique . En bref: ce modèle (avant Java5.0 ou sans l'instruction volatile) 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 réelle préoccupation. 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;
    }
}

1
C'est suffisant! Je suis juste à l'aise avec volatile et son utilisation. Oh, et trois acclamations pour JCiP.
Stu Thompson,

1
Oh, c'est apparemment l'approche préconisée par William Pugh, de la renommée de FindBugz.
Stu Thompson,

4
@Stu La première édition de Effective Java (copyright 2001) détaille ce modèle sous le point 48.
Pascal Thivent

9
@Bno: Que diriez-vous de rendre le constructeur privé?
xyz

2
@ AlikElzin-kilaka Pas tout à fait. L'instance est créée dans la phase de chargement de classe pour BarHolder , qui est retardée jusqu'à la première utilisation. Le constructeur de Bar peut être aussi compliqué que vous le souhaitez, mais il ne sera appelé que le premier getBar(). (Et s'il getBarest appelé "trop ​​tôt", vous rencontrerez le même problème, peu importe la façon dont les singleons sont implémentés.) Vous pouvez voir le chargement de classe paresseux du code ci-dessus ici: pastebin.com/iq2eayiR
Ti Strga

95

Thread safe en 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 volatilemodificateur ici. :) C'est important car sans lui, les autres threads ne sont pas garantis par le JMM (Java Memory Model) pour voir les changements de sa valeur. La synchronisation ne s'occupe pas de cela - elle ne sérialise que l'accès à ce bloc de code.

EDIT 2 : La réponse de @Bno détaille l'approche recommandée par Bill Pugh (FindBugs) et est sans doute meilleure. Allez lire et voter sa réponse aussi.


1
Où puis-je en savoir plus sur le modificateur volatil?
eleven81


2
Je pense qu'il est important de mentionner les attaques par réflexion. Il est vrai que la plupart des développeurs n'ont pas à s'inquiéter, mais il semble que des exemples comme ceux-ci (sur des singletons basés sur Enum) devraient inclure soit du code qui protège contre les attaques à instanciation multiple soit simplement mettre un avertissement indiquant de telles possibilités.
luis.espinal

2
Le mot-clé volatile n'est pas nécessaire ici - car la synchronisation donne à la fois l'exclusion mutuelle et la visibilité de la mémoire.
Hemant

2
Pourquoi s'embêter avec tout cela en Java 5+? Ma compréhension est que l'approche enum fournit à la fois la sécurité des threads et l'initialisation paresseuse. C'est aussi beaucoup plus simple ... De plus, si vous voulez éviter une énumération, j'empêcherais toujours l'approche de classe statique imbriquée ...
Alexandros

91

Oubliez l' initialisation paresseuse , 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;
    }
}

21
La variable d'instance singleton peut également devenir finale. par exemple, final statique statique A singleton = nouveau A ();
jatanp

19
C'est effectivement une initialisation paresseuse, car le singleton statique ne sera pas instancié tant que la classe n'est pas chargée et la classe ne sera pas chargée jusqu'à ce qu'elle soit nécessaire (ce qui correspondra exactement au moment où vous référencerez pour la première fois la méthode getInstance ()).
Dan Dyer

7
Si la classe A est chargée bien avant de vouloir instancier la statique, vous pouvez envelopper la statique dans une classe interne statique pour découpler l'initialisation de la classe.
Tom Hawtin - tackline

3
je suis d'accord que cette réponse est la plus simple, et Anirudhan, il n'est pas nécessaire de déclarer l'instance finale. Aucun autre thread n'aura accès à la classe pendant l'initialisation des membres statiques. cela est garanti par le compilateur, en d'autres termes, toute l'initialisation statique se fait de manière synchronisée - un seul thread.
Inor

4
Cette approche a une limitation: le constructeur ne peut pas lever d'exception.
wangf

47

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 fondamentalement mauvais, 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épendances plus utile, en particulier si vous utilisez également des tests unitaires, car DI vous permet d'utiliser des ressources simulées à des fins de test.


vous pouvez également injecter des valeurs fictives avec la méthode traditionnelle mais je suppose que ce n'est pas la manière standard / srping donc son travail supplémentaire avec seulement le gain étant le code hérité ...
tgkprog

21

Je suis mystifié par certaines des réponses qui suggèrent DI comme une 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, sauf l'exemple de code le plus trivial) de:

  1. Utilisez un framework DI comme Spring, puis
  2. Intégrez-le à votre configuration DI, que vos dépendances soient des singletons, une étendue de demande, une étendue 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 tous les singletons que vous utilisez soient threadsafe, bien sûr).


4
Peut-être parce que les gens ne sont pas d'accord avec vous. Je ne vous ai pas déçu, mais je ne suis pas d'accord: je pense que DI peut être utilisé pour résoudre les mêmes problèmes que les singletons. Ceci est basé sur la compréhension de "singleton" pour signifier "un objet avec une seule instance qui est accessible directement par un nom global", plutôt que juste "un objet avec une seule instance", ce qui est peut-être un peu délicat.
Tom Anderson

2
Pour développer cela légèrement, considérons une TicketNumbererqui doit avoir une seule instance globale et où vous voulez écrire une classe TicketIssuerqui contient une ligne de code int ticketNumber = ticketNumberer.nextTicketNumber();. Dans la pensée singleton traditionnelle, la ligne de code précédente devrait ressembler à quelque chose TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;. Dans la pensée DI, la classe aurait un constructeur comme public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }.
Tom Anderson

2
Et cela devient le problème de quelqu'un d'autre d'appeler ce constructeur. Un cadre DI le ferait avec une carte globale quelconque; une architecture DI construite à la main le ferait parce que la mainméthode de l'application (ou l'un de ses serviteurs) créerait la dépendance, puis appellerait le constructeur. Essentiellement, l'utilisation d'une variable globale (ou d'une méthode globale) n'est qu'une simple forme du modèle de localisateur de service redouté et peut être remplacée par une injection de dépendance, tout comme toute autre utilisation de ce modèle.
Tom Anderson

@ TomAnderson Je ne comprends vraiment pas pourquoi les gens «redoutent» le modèle de localisation de service. Je pense que dans la plupart des cas, c'est trop ou pas nécessaire au mieux, cependant, il y a des cas apparemment utiles. Avec un plus petit nombre de paramètres, DI est définitivement préféré, mais imaginez 20+. Dire que le code n'est pas structuré n'est pas un argument valide, car parfois les regroupements de paramètres n'ont tout simplement aucun sens. De plus, du point de vue des tests unitaires, je ne me soucie pas de tester le service, juste la logique métier de celui-ci, et s'il est bien codé, ce serait facile. Je n'ai vu ce besoin que dans des projets à très grande échelle.
Brandon Ling

20

Voyez 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 très facilement trébucher si vous google singletons en Java.

Personnellement, j'essaie d'éviter les singletons aussi souvent que possible pour de nombreuses raisons, dont la plupart peuvent être trouvées en recherchant des singletons sur Google. 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 mécanisme pour obtenir des données "globales" dans une conception OO et ils sont utilisés parce qu'il est facile de contourner la gestion du cycle de vie des objets (ou vraiment penser à comment 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 bon compromis.

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


D'accord. Il s'agit plus d'une classe fondamentale qui lance le reste de votre application et si elle était dupliquée, vous vous retrouverez avec un chaos complet (c'est-à-dire un accès unique à une ressource ou l'application de la sécurité). La transmission de données globales dans toute votre application est un gros drapeau rouge de couplage. Utilisez-le lorsque vous reconnaissez en avoir vraiment besoin.
Salvador Valence

16

Voici 3 approches différentes

1) Enum

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

2) Double 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;
    }
}

13

J'utilise le 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 de toute façon si plusieurs chargeurs de classe 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.


11

Wikipedia a quelques exemples de singletons, également en Java. L'implémentation de Java 5 semble assez complète et est compatible avec les threads (verrouillage vérifié deux fois).


11

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-safe 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-blocage, haute performance.


10

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


2
La première version est la meilleure. En supposant que la classe ne fait rien d'autre que de fournir un singleton, il sera généralement instancié à peu près au même point que celui de la deuxième version en raison du chargement de classe paresseux.
Dan Dyer

1
La double vérification est inutile pour un statique. Et pourquoi avez-vous rendu publique la méthode du clone protégé?
Tom Hawtin - tackline

1
-1 votre version de verrouillage à double vérification est cassée.
assylias

5
Vous devez également créer votre variable singletonvolatile
MyTitle

La première version est paresseuse et thread-safe.
Miha_x64

9

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, vous devez garantir 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 à implémenter et ne présente aucun inconvénient concernant les objets sérialisables, qui doivent être contournés dans les 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 facile que d'appeler la getInstance()méthode 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 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, ObjectInputStreamlit le nom de la constante dans le flux; la constante désérialisée est alors obtenue en appelant lajava.lang.Enum.valueOf méthode, en passant le type d'énumération 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 les cibles de références arrières apparaissant ultérieurement dans le flux de sérialisation.

Le processus par lequel les constantes ENUM sont sérialisés ne peut être personnalisé: une classe spécifique writeObject, readObject, readObjectNoData, writeReplace, et readResolveméthodes définies par les types enum sont ignorés pendant la sérialisation et la désérialisation. De même, les déclarations any serialPersistentFieldsou serialVersionUIDfield sont également ignorées - tous les types d'énumération ont un fixe serialVersionUIDde 0L. Il n'est pas nécessaire de documenter les champs et les données sérialisables pour les types d'énumération, car il n'y a pas de variation dans le type de données envoyées.

Extrait de documents Oracle

Un autre problème avec les Singletons conventionnels est qu'une fois que vous implémentez l' Serializableinterface, ils ne restent plus Singleton car la readObject()méthode retourne toujours une nouvelle instance comme constructeur en Java. Cela peut être évité en utilisant readResolve()et en supprimant 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. Motif singleton
  2. Enums, singletons et désérialisation
  3. Verrouillage à double vérification et motif Singleton

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

(1) n'est pas enthousiaste, il est paresseux en raison du mécanisme de chargement de classe JVM.
Miha_x64

@ Miha_x64 Quand ai-je dit un chargement impatient, j'ai dit une initialisation désirée.
Dheeraj Sachan

Java efficace est un excellent livre, mais nécessite certainement une édition.
Miha_x64

@ Miha_x64 ce qui est un chargement impatient, pouvez-vous expliquer par exemple
Dheeraj Sachan

Faire quelque chose «avec impatience» signifie «dès que possible». Par exemple, Hibernate prend en charge le chargement des relations avec impatience, si cela est explicitement requis.
Miha_x64 du

8

Peut-être un peu tard 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 répétons 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 une méthode statique publique appelée getInstance (). La première fois que getInstance () est appelée, le membre INSTANCE est nul. Le flux tombera alors dans la condition de création et créera une nouvelle instance de la classe MySingleton. Les appels suivants à getInstance () constateront que la variable 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 cette implémentation 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 environ), ils verront chacun le membre INSTANCE comme nul. Cela entraînera la création d'une nouvelle instance MySingleton par chaque thread et la définition ultérieure du membre 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 la méthode getInstance (). Cela corrigera certainement notre condition de course. Les threads vont maintenant se bloquer et entrer la méthode une par une. Mais cela crée également un problème de performances. 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 INSTANCE. Étant donné que les lectures constitueront la majeure partie de nos appels (rappelez-vous, l'instanciation ne se produit que lors du premier appel), nous subirons un impact de performance inutile en synchronisant la méthode entière.


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 encapsule 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 () en même temps ou à peu près en même temps et ils verront tous le membre INSTANCE comme nul. Ils frapperont alors 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 dans 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 un autre chèque à l'intérieur du bloc. Si le membre INSTANCE a déjà été défini, nous ignorerons l'initialisation. C'est ce qu'on appelle le verrouillage à double vérification.

Cela résout notre problème d'instanciation multiple. Mais encore une fois, notre solution a présenté un autre défi. D'autres threads peuvent ne pas «voir» que le membre 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 CPU. 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 implémentation singleton. Un deuxième thread - en cours de traitement par un autre processeur ou noyau, 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 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 INSTANCE. Cela indiquera au compilateur de toujours lire et d'é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 du processeur, nous prendrons un coup de performance à chaque fois que nous opérons sur le membre 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 marginal car nous ne créons l'instance que lors du premier appel de la méthode. Peut-être qu'un succès de performance à la création est tolérable. Mais même notre cas d'utilisation principal, reads, fonctionnera deux fois sur le membre volatil. 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;
}

Étant donné que la performance est due à un fonctionnement direct sur le membre volatil, définissons une variable locale à la valeur du volatile et opérons à la place sur la variable locale. Cela diminuera le nombre de fois où nous opérons sur le volatile, récupérant ainsi une partie de nos performances perdues. Notez que nous devons redéfinir notre variable locale lorsque nous entrons dans le bloc synchronisé. Cela garantit qu'il est à jour avec tous les changements survenus pendant que nous attendions le verrou.

J'ai récemment écrit un article à ce sujet. Déconstruire le Singleton . Vous pouvez trouver plus d'informations sur ces exemples et un exemple du modèle "titulaire" ici. Il existe également un exemple concret présentant l'approche volatile à double vérification. J'espère que cela t'aides.


Pourriez-vous s'il vous plaît expliquer pourquoi BearerToken instancedans votre article ne l'est pas static? Et c'est quoi result.hasExpired()?
Woland

Et qu'en est-il class MySingleton- peut-être que ça devrait être final?
Woland

1
@Woland L' BearerTokeninstance n'est pas statique car elle fait partie de la BearerTokenFactory - qui est configurée avec un serveur d'autorisation spécifique. Il peut y avoir de nombreux BearerTokenFactoryobjets - chacun ayant son propre «cache» BearerTokenqu'il distribue jusqu'à son expiration. La hasExpired()méthode sur BeraerTokenest appelée dans la get()méthode de l'usine pour s'assurer qu'elle ne distribue pas de jeton expiré. S'il expire, un nouveau jeton sera demandé au serveur d'autorisation. Le paragraphe suivant le bloc de code explique cela plus en détail.
Michael Andrews

4

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 paresseux 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();
    }
}

Les deux sont paresseux, en supposant que la seule chose dont vous avez besoin de singleton est son instance.
Miha_x64

@ Miha_x64 le premier cas instanciera le singleton lorsque la JVM initialise la classe, le second cas n'instanciera que le singleton lors de l'appel getInstance(). Mais en effet, si vous n'avez pas d'autres méthodes statiques dans votre classe Singletonet que vous appelez uniquement, getInstance()il n'y a pas de réelle différence.
Nicolas Filotto

3

Vous avez besoin de vérifier l' idiome si vous avez besoin de charger la variable d'instance d'une classe paresseusement. Si vous devez charger une variable statique ou un singleton paresseusement, vous avez besoin d'une initialisation sur le support de demande idiome du .

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éé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 s'y référant.

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

3

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 une classe statique interne peut être utilisée.


3

Enum singleton

La façon la plus simple d'implémenter un Singleton qui est thread-safe 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 dans Java 1.5

Verrouillage à double contrôle

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é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 assure la sécurité des threads.

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

}

2

Pour JSE 5.0 et versions ultérieures, adoptez l'approche Enum, sinon utilisez l'approche statique du titulaire singleton ((une approche de chargement paresseux décrite par Bill Pugh). La dernière solution est également thread-safe sans nécessiter de constructions de langage spéciales (c'est-à-dire volatiles ou synchronisées).


2

Un autre argument souvent utilisé contre les singletons est 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 setInstanceméthode ajoutée permet de définir une implémentation maquette de la classe singleton pendant les 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");
    }

}

Cela présente l'inconvénient d'exposer également cette fonctionnalité à l'application normale. D'autres développeurs travaillant sur ce code pourraient être tentés d'utiliser la méthode «setInstance» pour modifier altérer 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.

Pourtant, pour la possibilité de tests de maquette (si nécessaire), cette exposition au code peut être un prix acceptable à payer.


1

classe singleton la plus simple

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

1
c'est la même chose que la réponse de Jonathan ci
inor

1
Duplicata de cette réponse fraternelle de Jonathan publiée cinq ans plus tôt. Voir cette réponse pour des commentaires intéressants.
Basil Bourque

0

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

public enum Singleton{ INSTANCE; }

et vous avez terminé !!!


1
Cela est déjà mentionné dans d'autres réponses il y a des années.
Pang

0

Jetez un oeil à ce post.

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

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

Singleton (reconnaissable par les 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.Desktop # getDesktop ()
  • java.lang.System # getSecurityManager ()

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


0

Le meilleur motif singleton que j'ai jamais vu utilise l'interface fournisseur.

  • C'est générique et réutilisable
  • Il prend en charge l'initialisation paresseuse
  • Il est uniquement synchronisé jusqu'à ce qu'il soit 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();
    }
}

-3

Parfois, un simple " static Foo foo = new Foo();" ne suffit pas. Pensez simplement à l'insertion de données de base que vous souhaitez effectuer.

D'un autre côté, 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 des situations très très rares 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 via le chargeur de classe. Directement après que la classe a été interprétée à partir d'un tableau d'octets, la machine virtuelle exécute le bloc {} - statique . c'est tout le secret: le bloc statique n'est appelé qu'une seule fois, le moment où la classe (nom) donnée du paquet donné est chargée par ce chargeur de classe.


3
Pas vrai. les variables statiques sont initialisées avec les blocs statiques lorsque la classe est chargée. Pas besoin de diviser la déclaration.
Craig P. Motlin

-5
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é Synchronized avant getInstance, nous avons évité la condition de concurrence critique dans le cas où deux threads appellent getInstance en même temps.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.