Comment écrire un Singleton correctement?

Aujourd'hui, dans mon interview, un intervieweur m'a demandé d'écrire un cours de Singleton. Et j'ai donné ma réponse comme

public class Singleton {

    private static Singleton ref;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (ref == null) {
            ref = new Singleton();
        }
        return ref;
    }
}

soudain, il m'a dit que c'était une vieille façon d'écrire la classe. Peut-on s'il vous plaît aidez-moi pourquoi il a dit comme ça.

23
demandé sur Som 2013-09-17 20:45:00

13 réponses

La première chose qui me vient à l'esprit lors de la création d'un singleton est enum . J'utilise généralement le protocole enum pour mettre en œuvre singleton:

enum Singleton {
    INSTANCE;
}

un avantage que vous obtenez avec l'utilisation d'enum est avec la sérialisation.

avec la classe singleton, vous devez vous assurer que la sérialisation et la deserialisation ne créent pas une nouvelle instance en implémentant la méthode readResolve() , alors que ce n'est pas le cas avec enum.

En utilisant class vous devez créer le singleton comme ceci:

public final class Singleton implements Serializable {
    // For lazy-laoding (if only you want)
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
        if (SingletonHolder.INSTANCE != null) {
            // throw Some Exception
        }
    }

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

    // To avoid deserialization create new instance
    @SuppressWarnings("unused")
    private Singleton readResolve() {
        return SingletonHolder.INSTANCE;
    }
}
41
répondu Rohit Jain 2013-09-17 16:53:18

Dernières Solutions Standard:

  • Core java with Managed Beans / CDI

    @ApplicationScoped
    public class MySingleton { ... }
    
  • EJB Lite (JEE6)

    @Singleton
    public class MySingleton { ... }
    

recommandation antérieure (de "Java efficace 2"):

  • utiliser un enum, avec une seule constante de dénombrement fictive, p.ex. INSTANCE. Les champs de données et les méthodes peuvent être statiques ou non statiques (instance) - les deux se comportent de manière équivalente, puisqu'il n'y a qu'une instance

  • avantages:

    • Oui, c'est un singleton. Impossible de créer plus d'une instance par n'importe quel mécanisme (à l'intérieur d'un chargeur de classe donné sur n'importe quelle JVM).
    • l'initialisation de Singleton est sans fil.
  • inconvénients (par rapport aux Solutions Standard ci-dessus):

    • la définition est un peu vague - l'enveloppe extérieure d '"enum" pourrait induire en erreur les responsables inexpérimentés de la maintenance du code. C'est un petit piratage, pas l'intention originale d'enum.
    • l'implémentation fuit à travers l'abstraction. Elle viole le Uniforme Principe De L'Accès Aux
      • Ça ne fonctionne pas via l'injection de l' le client doit savoir qu'il est un singleton & code contre le précise enum instance
      • le client doit utiliser Singleton.INSTANCE.someMethod ()
      • le client ne peut pas (simplement) coder contre une interface
      • le client est touché par les changements de conception entre singleton et / ou les objets multi-instances
    • étend une classe d'ancêtre (Enum), empêchant l'héritage d'autres classes
    • Serialization & deserialization vient de transférer le nom de la constante enum sans état-bon pour s'assurer techniquement qu'il n'y a qu'une seule instance, mais une opération nulle en ce qui concerne les données. Dans le cas (rare) où l'on voulait partager/synchroniser l'état singleton à travers les chargeurs JVM/class, la réplication via la sérialisation ne pouvait pas être utilisée.
14
répondu Glen Best 2013-11-14 03:14:13

Vous pouvez le faire

public enum Singleton {
    INSTANCE;
}

et pour une classe d'utilité qui n'a pas d'instances

public enum Utility {
     ;

     public static void method();
}
10
répondu Peter Lawrey 2013-09-17 16:47:08

Comme d'autres l'ont déjà souligné, les enum modèle est désormais largement considérée comme une meilleure approche du Singleton, contre la vieille école, mais je voulais juste souligner un inconvénient.

Nous avons eu un Singleton dans la forme de:

public enum Foo {
    INSTANCE;
}

qui avait été autour pendant un certain temps, travaillant très bien. Puis, lors d'une révision du code, nous avons vu ceci:

public enum Foo {
    INSTANCE,
    ANOTHER;
}

après que nous l'avons frappé à travers le visage avec un maquereau humide, le codeur en question avait vu l'erreur de ses façons, et une plus grande que petite quantité de code a dû être retirée et/ou réécrite. Oui, nous l'avons attrapé avant sa sortie en production, mais il a fallu travailler pour l'effacer.

je pense que c'est une faiblesse de ce type de Singleton (bien que petit et peut-être rare) par rapport à la vieille école. Oui, vous pouvez briser n'importe quel modèle en le mettant mal en œuvre, mais il semble un enfer de beaucoup plus facile pour un codeur de briser un Singelton enum qu'un vieux Singleton bien formé.

EDIT:

pour être complet, voici un Enum Singleton qui protège contre des valeurs supplémentaires à ajouter plus tard:

public enum Foo
{
  INSTANCE;
  // adding another type here will cause a runtime

  static
  {
    if (Foo.values().length != 1)
    {
      throw new IllegalStateException("Not a Singleton.");
    }
  }
}
8
répondu splungebob 2013-09-17 18:49:50

c'est parce que votre solution n'est pas threadsafe.

la manière moderne est de lier l'instance à une valeur enum :

enum Singleton {
    INSTANCE;
}

si vous voulez utiliser lazy init de l'instance, alors vous pouvez utiliser le ClassLoader pour garantir la sécurité du filetage:

public class Singleton {
        private Singleton() { }

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

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

plus d'information sur Wikipedia

3
répondu Boris the Spider 2013-09-17 16:49:49

il cherche probablement cette réponse:

public class Singleton 
{
   private static Singleton ref;
   static
   {
       ref = new Singleton();
   }
   private Singleton()
   {
   }
   public static Singleton getInstance() 
   {
       return ref;
   }
}

remarquez le bloc statique. Cette approche est probablement lourde puisque l'instance est créée lors du chargement de la classe.

3
répondu Ace 2013-09-17 16:50:17

le Singleton que j'écrirais ressemblerait à ceci:

@Service
class PersonService {
    // implementation here
}

mais j'aime aussi les idées d'enum. En réalité, je n'écris jamais (et je n'ai pas besoin) un Singleton autre que celui ci-dessus.

3
répondu Erik Pragt 2013-09-17 16:51:16

Pourquoi ne pouvez-vous pas faire juste

public class SingletonSandBox {

    private static SingletonSandBox instance = new SingletonSandBox();

    private SingletonSandBox(){
    }

    public static SingletonSandBox getInstance(){
        return instance;
    }

}

et essai

public static void main(String[] args) {

        SingletonSandBox sss1 = SingletonSandBox.getInstance();

        SingletonSandBox sss2 = SingletonSandBox.getInstance();

        System.out.println(sss1 == sss2);

}

comme je le sais c'est sans fil et plus court que l'utilisation de bloc statique. Nouveau champ statique déclaration est lue plus tôt comparaison statique bloc par le runtime.

3
répondu Anton 2013-11-19 00:33:22

parfois, nous pourrions avoir à créer une instance d'une sous-classe seulement comme classe Java Toolkit. exemple 1: la création d'une seule instance d'une sous-classe est en cours d'élaboration. Veuillez vous référer à: Singleton Design Patter in Java

3
répondu Sanjay Madnani 2016-06-06 19:22:34

à Partir de Ce qui est un moyen efficace de mettre en œuvre un pattern singleton en Java?

utiliser un enum:

 public enum Foo 
 {
   INSTANCE;
 }

Joshua Bloch a expliqué cette approche dans son livre 'Effective Java'

aussi vérifier le meilleur modèle Java singleton de nos jours?

2
répondu Trey Jonn 2017-05-23 10:30:01

une version sans fil de L'approche initiale OPs, plus personne d'autre n'a osé Suggérer une déclaration synchronisée.

final class Singleton
{
    private static Object lock = new Object();
    private static volatile Singleton instance = null;
    private Singleton() { }
    public static Singleton getInstance()
    {
        if(instance == null)
        {
            synchronized(lock)
            {
                if(instance == null)
                {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
2
répondu Louis Ricci 2013-11-19 18:47:55

Ce singleton mise en œuvre est appelée,

initialisation

mais le problème est que cette implémentation n'est pas sécurisée.

ici , vous pouvez trouver le meilleur thread-safe implementation.

il y a aussi peu d'autres implémentations Singleton populaires. L'un est,

Désireux d'initialisation

final class EagerIntializedSingleton  {
    private static final EagerIntializedSingleton instance = new EagerIntializedSingleton();
    private EagerIntializedSingleton (){}
    private static EagerIntializedSingleton getInsance() {
        return instance;
    }  
}

mais ici, l'instance de la classe singleton est créée au moment du chargement de la classe. (C'est la classe par défaut singleton qui est créée par IntelliJ IDE)

Le côté populaire de la mise en œuvre est,

initialisation du bloc statique

private static StaticBlockSingleton instance;
private StaticBlockSingleton(){}
static {
    try {
         instance = new StaticBlockSingleton();
    catch(Exception e) {
         .............
    }
}

cette implémentation est similaire à l'initialisation eager, sauf que l'instance de la classe est créée dans le bloc statique qui fournit l'option "Traitement d'exception". L'initialisation impatiente et l'initialisation par bloc statique créent l'instance avant même qu'elle ne soit utilisée et ce n'est pas la meilleure pratique à utiliser.

2
répondu Vidura Mudalige 2017-05-23 12:34:57

cela pourrait être parce qu'il n'utilise pas " double-check-locking "(comme d'autres l'ont dit) ou cela pourrait aussi être parce qu'il est apparemment possible d'invoquer un constructeur privé en utilisant la réflexion (si la Politique de sécurité le permet).

pour invoquer un constructeur sans paramètres passer un tableau vide.

package org.example;

public class Singleton {

    private static final Object LOCK = new Object();
    private static final Singleton SINGLETON = new Singleton();
    private static volatile boolean init = false; // 'volatile' to prevent threads from caching state locally (prevent optimizing) 

    private Singleton() {
        synchronized (LOCK) {
            if( init == true) {
                throw new RuntimeException("This is a singleton class!");
            }
            init=true;
        }
    }

    public static Singleton obtainClassInstance() {
        return SINGLETON;
    }

}


package org.example;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SimpleSingletonTester {

    /**
     * @param args
     * @throws NoSuchMethodException 
     * @throws SecurityException 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws InstantiationException 
     * @throws IllegalArgumentException 
     */
    public static void main(String[] args) throws SecurityException, NoSuchMethodException, 
    IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException 
    {

        Class[] parameterTypes = {};
        Object[] initargs = {};
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(parameterTypes);
        System.out.println( constructor.isAccessible() );
        constructor.setAccessible(true);
        System.out.println( constructor.isAccessible() );
        System.out.println( constructor.newInstance(initargs) );
        System.out.println( constructor.newInstance(initargs) );

    }

}
1
répondu Ravindra HV 2017-05-23 11:55:16