Quelle est une façon efficace de mettre en œuvre un modèle singleton en Java? [fermé]
30 réponses
utiliser un enum:
public enum Foo {
INSTANCE;
}
Joshua Bloch a expliqué cette approche dans son Java Effective Reloaded talk at Google I/O 2008: link to video . Voir aussi les diapositives 30-32 de sa présentation ( effective_java_relevé.pdf ):
La bonne Façon de mettre en Œuvre un Serializable Singleton
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 à l'approche de terrain public, sauf qu'elle est plus concise, fournit le mécanisme de sérialisation gratuitement, et fournit une garantie sans faille contre les instanciations multiples, même en cas d'attaques sophistiquées de sérialisation ou de réflexion. Bien que cette approche n'ait pas encore été largement adoptée, le type single-element enum est la meilleure façon d'implémenter un singleton ."
selon l'usage, il y a 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");
}
}
voyons 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 faire le constructeur privé pour empêcher les utilisateurs de créer leur propre Foo. Lancer une exception du constructeur empêche les utilisateurs d'utiliser la réflexion pour créer un second Foo. Ensuite vous créez un champ private static final Foo
pour tenir la seule instance, et une méthode public static Foo getInstance()
pour la retourner. 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 disposez D'un objet de très grande taille ou d'un code de construction lourd et que vous disposez également d'autres méthodes ou champs statiques accessibles qui pourraient être utilisés avant qu'une instance est nécessaire, alors, et seulement alors vous devez utiliser l'initialisation tardive.
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;
}
}
puisque la ligne private static final Foo INSTANCE = new Foo();
n'est exécutée que lorsque la classe FooLoader est effectivement utilisée, cela prend soin de l'instanciation paresseuse, et est-il garanti d'être filé en toute sécurité.
quand vous voulez aussi 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 retournée, même si l'objet a été sérialisé lors d'un passage précédent de votre programme.
Avertissement: je viens de résumer l'ensemble de l'génial réponses et il a écrit à mes paroles.
"tout en mettant en œuvre Singleton, nous avons 2 options
1. Chargement paresseux
2. Chargement anticipé
chargement paresseux ajoute peu au-dessus (beaucoup de pour être honnête) donc utilisez-le seulement quand vous avez un très grand objet ou code de construction lourde et aussi avoir d'autres méthodes statiques accessibles ou des champs qui pourraient être utilisés avant qu'une instance soit nécessaire, alors et seulement alors vous devez utiliser l'initialisation paresseuse.Sinon, le choix d'un chargement anticipé est un bon choix.
la manière la 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 est bon sauf son premier singleton chargé. Essayons paresseux chargé singleton
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 si bon mais notre héros ne survivra pas tandis que se battant seul avec des fils maléfiques multiples qui veulent beaucoup d'instance de notre héros. Donc permet 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;
}
}
cela s'appelle"double vérification de L'idiome de verrouillage". Il est facile d'oublier la déclaration volatile et difficile de comprendre pourquoi elle est nécessaire.
Pour details: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
maintenant nous sommes sûrs au sujet du fil diabolique mais qu'en est-il de la sérialisation cruelle? Nous devons nous assurer que même si de-serialiaztion aucun nouvel objet n'est créé
class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static volatile Foo INSTANCE = null;
// Rest of the things are same as above
// No more fear of serialization
@SuppressWarnings("unused")
private Object readResolve() {
return INSTANCE;
}
}
la méthode readResolve()
s'assurera que la seule instance sera retournée, même si l'objet a été sérialisé lors d'un passage précédent de notre programme.
enfin nous avons ajouté assez de protection contre les threads et la sérialisation mais notre code semble encombrant et laid. Donnons à notre héros un make over
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 très même héros :)
Puisque 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 d'être sans fil.
et nous sommes venus si loin, voici le meilleur moyen d'atteindre tout ce que nous avons fait est le meilleur moyen possible
public enum Foo {
INSTANCE;
}
qui seront traitées à l'interne comme
public class Foo {
// It will be our sole hero
private static final Foo INSTANCE = new Foo();
}
ce n'est plus la peur de la sérialisation, des fils et du code laid. Aussi enums singleton sont paresseusement initialisés .
Cette approche est fonctionnellement équivalente à la approche de terrain public, sauf qu'il est plus concis, fournit la sérialisation des machines gratuitement, et offre une solide garantie contre de multiples instanciation, même face à une sérialisation sophistiquée ou attaques de réflexion. Bien que cette approche doit encore être largement adopté, un enum à un seul élément est la meilleure façon d'implémenter un singleton.
- Joshua Bloch dans "Effective Java"
Maintenant vous pourriez ont réalisé pourquoi ENUMS sont considérés comme la meilleure façon de mettre en œuvre Singleton et merci pour votre patience:)
Mis à jour sur mon blog .
la solution Postée par Stu Thompson est valide en Java5.0 et plus tard. Mais je préférerais ne pas l'utiliser parce que je pense qu'il est sujet aux erreurs.
il est facile d'oublier l'énoncé volatile et difficile de comprendre pourquoi il est nécessaire. Sans le volatile, ce code ne serait plus sûr à cause de l'antipattern de verrouillage double-vérifié. Pour en savoir plus à ce sujet, voir le paragraphe 16.2.4 de "Java Competiency in Practice . En bref: Cette motif (avant Java5.0 ou sans la déclaration volatile) pourrait renvoyer une référence à L'objet barre qui est (encore) dans un état incorrect.
Ce modèle a été inventé pour l'optimisation des performances. Mais ce n'est vraiment pas un souci réel plus. La suite paresseux code d'initialisation est rapide et surtout plus facile à lire.
class Bar {
private static class BarHolder {
public static Bar bar = new Bar();
}
public static Bar getBar() {
return BarHolder.bar;
}
}
Thread safe in 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;
}
}
modifier : faites attention au modificateur volatile
ici. :) Il est important parce que sans lui, d'autres threads ne sont pas garantis par le JMM (Java Memory Model) pour voir les changements de sa valeur. La synchronisation n'est pas prendre soin de cela, il ne sérialise 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 discutable mieux. Allez lire et de voter sa réponse.
Oublier 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;
}
}
assurez-vous que vous en avez vraiment besoin. Faites un google pour "singleton anti-pattern" pour voir quelques arguments contre elle. Il n'y a rien de mal à cela, je suppose, mais c'est juste un mécanisme pour exposer certaines 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, surtout si vous utilisez également des tests unitaires, CAR DI vous permet d'utiliser des ressources moquées à des fins de test.
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.
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 soit des instances singleton ou non-singleton (par exemple per-thread). Au moins c'est vrai si vous utilisez Spring 2.x, Je ne peux pas parler pour d'autres cadres DI.
donc ma réponse au PO serait (sauf pour le code échantillon le plus trivial) à:
- utilisez un cadre DI comme le printemps, puis
- faites en sorte que cela fasse partie de votre configuration DI que vos dépendances soient des singletons, des requêtes scopées, des sessions scopées, ou n'importe quoi d'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 de mise en œuvre facilement réversible (à condition que les Singleton que vous utilisez sont threadsafe, bien sûr).
pensez 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 éviter si vous utilisez Google singletons en Java.
personnellement, J'essaie d'éviter les singletons aussi souvent que possible pour de nombreuses raisons, encore une fois la plupart d'entre elles 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 les 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 de l'objet (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 avez vraiment besoin d'un alors wikipedia a un bon exemple d'une mise en œuvre correcte d'un singleton.
suit trois approches différentes
1) Enum
/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
INSTANCE;
}
2 )Double Contrôle De 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 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;
}
}
j'utilise le cadre à ressort pour gérer mes Singleton. Il n'impose 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 vraiment facile de construire et configurer différentes usines pour créer différents types d'objets.
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, filet de sécurité 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, filet de sécurité avec non-blocage, haute performance.
si vous n'avez pas besoin de chargement paresseux alors 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 sans fil, essayez le modèle à 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 schéma de double vérification n'est pas garanti pour fonctionner (en raison d'un problème avec les compilateurs, Je ne sais pas 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.
je dirais Enum singleton
Singleton utilisant enum en Java est généralement une façon de déclarer enum singleton. Enum singleton peut contenir la variable d'instance et la 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'un enum est très facile à mettre en œuvre et n'a pas d'inconvénients concernant serializable les objets, qui doivent être contournés d'une autre manière.
/**
* 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 getInstance()
méthode sur Singleton.
1.12 sérialisation des constantes D'Énum
les constantes Enum sont sérialisées différemment des objets ordinaires sérialisables ou externalisables. La forme sérialisée d'une constante enum se compose uniquement de son nom; les valeurs de champ de les constantes ne sont pas présent dans le formulaire. Pour sérialiser une constante enum,
ObjectOutputStream
écrit la valeur retournée par la méthode du nom de la constante enum. Pour désérialiser une constante enum,ObjectInputStream
lit le nom de la constante à partir du flux; la constante désérialisée est alors obtenue en appelant la méthodejava.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 d'enum peuvent fonctionner comme les cibles de références antérieures apparaissant par la suite dans le flux de sérialisation.le processus par lequel les constantes enum sont sérialisées ne peut pas être personnalisé:
writeObject
,readObject
,readObjectNoData
,writeReplace
, et les méthodesreadResolve
définies par les types enum sont ignorées lors de la sérialisation et de la desérialisation. De même, toute déclaration de zoneserialPersistentFields
ouserialVersionUID
est également ignorée--tous les types enum ont unserialVersionUID
fixe de0L
. Documenter les champs et les données sérialisables pour les types d'enum n'est pas nécessaire, car il n'y a pas de variation dans le type de données envoyées.
un autre problème avec les Singletons conventionnels est qu'une fois que vous mettez en œuvre Serializable
interface, ils ne restent plus Singleton parce que readObject()
méthode toujours retourner une nouvelle instance comme constructeur en Java. Cela peut être évité en utilisant readResolve()
et en écartant 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, comme vous avez besoin de les rendre transitoires, mais avec Enum Singleton, la sérialisation est garantie par JVM.
Bonne Lecture
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");
}
}
est peut-être un peu en retard, 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érer sur le problème. Vous verrez ce que je veux dire.
la première tentative pourrait ressembler à quelque chose comme ceci:
public class MySingleton {
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
...
}
ici nous avons le Classe MySingleton qui a un membre statique privé appelé INSTANCE, et une méthode statique publique appelée getInstance(). La première fois que gettinstance() est appelée, le membre D'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 à gettinstance () constateront que la variable D'INSTANCE est déjà définie, et donc ne pas créer une autre instance MySingleton. Ceci assure Qu'il n'y a qu'une seule instance de MySingleton qui est partagé entre tous les appelants de getInstance().
mais cette implémentation a un problème. Les applications multi-threadées auront une condition de course lors de la création de l'instance unique. Si plusieurs threads d'exécution frappent la méthode getInstance () à (ou autour) au même moment, ils verront chacun le membre D'INSTANCE comme null. Il en résultera que chaque thread créera une nouvelle instance MySingleton et définira par la suite le 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é synchronisé dans la signature de la méthode pour synchroniser la méthode getInstance (). Cela va certainement corriger notre condition de course. Threads maintenant bloquer et entrer dans la méthode. Mais cela crée aussi 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 INSTANCE. Puisque les lectures constitueront la majeure partie de nos appels (rappelez-vous, l'instanciation ne se produit que lors du premier appel), nous serons confrontés à une performance inutile en synchronisant l'ensemble de 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 à un bloc synchronisé qui enveloppe la création de L'instance MySingleton. Mais est-ce que cela résout notre problème? Nous ne bloquons plus les lectures, mais nous avons aussi pris un pas en arrière. Plusieurs threads vont frapper la méthode getInstance () au même moment ou à peu près au même moment et ils verront tous le membre de L'INSTANCE comme null. Ils vont ensuite frapper le bloc synchronisé où l'on obtiendra la serrure et créer l'instance. Quand ce thread sort du bloc, les autres threads lutteront pour la serrure, et un par un chaque thread tombera à travers le bloc et créera une nouvelle instance de notre classe. Nous sommes donc de retour au point de départ.
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 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 contrôle.
cela résout notre problème d'instanciation multiple. Mais une fois de plus, notre solution a présenté un autre défi. D'autres threads pourraient 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 fils copient les valeurs originales de variables de la mémoire principale dans le cache du CPU. Les changements de valeurs sont ensuite écrites et lues à partir du cache. C'est une fonctionnalité de Java conçu pour optimiser les performances. Mais cela crée un problème pour notre implémentation singleton. Un second thread - en cours de traitement par un CPU ou un noyau différent, en utilisant un cache différent - ne verra pas les modifications apportées par le premier. Cela provoquera le second thread à voir le membre de L'INSTANCE comme null forçant une nouvelle instance de notre singleton à être créée.
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 D'INSTANCE. Cela indiquera au compilateur de toujours lire à partir de la mémoire principale et d'écrire dans celle-ci, et non dans le cache CPU.
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 deux fois l'existence (1 et 2), définissez la valeur (3), et ensuite de retour à la valeur (4). On pourrait argumenter que ce chemin est le cas fringe puisque nous ne créons l'instance que durant le premier appel de la méthode. Peut-être qu'un succès de création est tolérable. Mais même notre principal cas d'utilisation, lit, opérera sur le membre volatile deux fois. Une fois pour vérifier l'existence, et encore pour rendre 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 la performance hit est due à opérer directement sur le membre volatile, définissons une variable locale à la valeur de la variable volatile et opérons sur la variable locale à la place. Cela réduira le nombre de fois où nous opérons sur le volatile, récupérant ainsi une partie de notre performance perdue. Notez que nous devons définir à nouveau notre variable locale lorsque nous entrons dans le bloc synchronisé. Cela garantit qu'il est à jour avec tous les changements qui se sont produits pendant que nous attendions la serrure.
j'ai écrit un article à ce sujet récemment. Déconstruisant Le Singleton . Vous pouvez trouver plus d'informations sur ces exemples et un exemple de "titulaire" modèle là. Il y a aussi un exemple concret qui illustre l'approche volatile à double contrôle. Espérons que cette aide.
Vous avez besoin de double contrôle l'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 initilization à la demande du titulaire l'idiome.
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 d'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 (), qui a forcé ce nouvel objet à être un déchet collecté 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.
}
les Différents moyens pour objet singleton:
-
selon Joshua Bloch - Enum serait le meilleur.
-
vous pouvez également utiliser le double contrôle de verrouillage.
-
même la classe statique intérieure peut être utilisée.
Enum singleton
la façon la plus simple de mettre en œuvre un Singleton qui est sans fil est d'utiliser un Enum
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("This is a singleton");
}
}
ce code fonctionne depuis L'introduction D'Enum en Java 1.5
double contrôle de verrouillage
"si vous voulez coder un singleton" classique " qui fonctionne dans un environnement multithreaded (à partir de Java 1.5), vous devez utiliser celui-ci.
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
ce n'est pas thread-safe avant 1.5 parce que la mise en œuvre du mot-clé volatile était différente.
en charge Précoce Singleton (fonctionne même avant de Java 1.5)
cette implémentation instancie le singleton lorsque la classe est chargée et assure 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");
}
}
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 paresseux 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();
}
}
pour JSE 5.0 et au-dessus, suivre L'approche Enum, sinon utiliser l'approche statique à un seul titulaire ( (une approche de chargement paresseux décrite par Bill Pugh). Cette dernière solution est également thread-safe sans nécessiter de constructions linguistiques spéciales (c'est-à-dire volatiles ou synchronisés).
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, je voudrais faire quelques légères modifications:
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 de maquette de la classe singleton lors des tests:
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
cela fonctionne aussi avec des 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");
}
}
Ce qui a l'inconvénient d'exposer 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 une fonction spécifique et donc changer le comportement de l'application dans son ensemble, par conséquent cette méthode devrait contenir au moins un bon avertissement dans son javadoc.
encore, pour la possibilité de test de maquette (si nécessaire), cette exposition de code peut être un prix acceptable pour payer.
classe singleton la plus simple
public class Singleton {
private static Singleton singleInstance = new Singleton();
private Singleton() {}
public static Singleton getSingleInstance() {
return singleInstance;
}
}
je pense toujours qu'après java 1.5, enum est la meilleure implémentation singleton disponible car elle garantit que même dans les environnements multi - threadés-une seule instance est créée.
public enum Singleton{
INSTANCE;
}
et c'est fini !!!
Avoir un coup d'oeil à ce post.
exemples de Modèles de conception GoF dans les bibliothèques de base de Java
De la meilleure réponse est "Singleton",
Singleton (reconnaissable par des méthodes créationnelles retournant la même instance (généralement d'elle-même) chaque fois)
- de java.lang.Runtime#getRuntime ()
- Java.awt.Desktop#getDesktop ()
- de java.lang.System#getSecurityManager()
vous pouvez également apprendre L'exemple de Singleton de Java native classes eux-mêmes.
le meilleur modèle singleton que j'ai jamais vu utilise l'interface Fournisseur.
- c'est générique et réutilisable
- il supporte l'initialisation paresseuse
- il est seulement synchronisé jusqu'à ce qu'il ait été initialisé, puis le fournisseur de blocage est remplacé par un non-fournisseur de blocage.
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();
}
}
parfois un simple " static Foo foo = new Foo();
" ce n'est pas assez. Pensez à quelques insertions de données de base que vous voulez faire.
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 conduire à des problèmes de performances ou de verrouillage (dans des situations 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;
}
}
et maintenant? Le la classe est chargé par le chargeur de classes. Directement après que la classe a été interprétée à partir d'un tableau d'octets, la VM exécute le bloc statique { } -. c'est tout le secret: le Static-block n'est appelé qu'une seule fois, la fois où la classe donnée (nom) du paquet donné est chargée par ce chargeur de classe unique.
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 course dans le cas où deux threads appellent le getInstance en même temps.