web-dev-qa-db-fra.com

Motif Singleton avec une combinaison de chargement paresseux et de sécurité du fil

Je faisais des recherches sur les singletons, en particulier sur l'initialisation paresseuse ou désirée des singletons.

Un exemple d'initialisation désirée:

public class Singleton
{
    //initialzed 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;
    }
}

mais comme indiqué ci-dessus, il s’agit d’une initialisation rapide et la sécurité des threads est laissée à jvm

alors je viens avec cette approche: 

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

Comme indiqué ci-dessus depuis la ligne 

private static final Foo INSTANCE = new Foo(); 

n’est exécuté que lorsque la classe FooLoader est réellement utilisée, elle s’occupe de l’instanciation paresseuse et garantit la sécurité des threads.

Est-ce correct?

20
tuntun fdg

Votre deuxième extrait de code est, à mon avis, le meilleur moyen d’initialiser un singleton en toute sécurité. Il a en fait un nom de modèle

Idiome de titulaire d'initialisation à la demande

Je vous suggère de l'utiliser.

18
John Vint

Votre première conception est en réalité paresseuse. Pensez-y, l'instance n'est créée que lorsque la classe est initialisée; la classe n'est initialisée que lorsque la méthode getSingleton() est appelée [1]. Ainsi, l’instance n’est créée que sur demande, c’est-à-dire créée paresseusement.

[1] http://docs.Oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

6
ZhongYu

Le second est très mauvais en termes de lisibilité, le premier convient. Regardez cet article article . Il s’agit d’un verrouillage à double contrôle, mais vous donnera également de nombreuses informations sur le multithreading de singletons.

2
Mikhail

Le meilleur moyen est d’utiliser le Enum Way :

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
            //... perform operation here ...
    }
}
1
Jean Logeart

À mon avis, ceci est un modèle inapproprié à utiliser. Il émet des hypothèses sur le comportement de la machine virtuelle qui sont non triviales et déroutantes. En outre, il a une classe factice. Les cours factices doivent être évités autant que possible.

Je suggère l'approche simple:

public class Foo {
    private volatile static final Foo instance = null;

    private Foo() {
    }

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

... bien que cela ne fonctionne pas tel quel - ce n'est pas sécurisé du point de vue des threads. Ce que vous voulez vraiment, c'est le motif de double vérification présenté à la rubrique 71 de Bloch's Effective Java; voir ici . En adaptant l'exemple du lien vers votre cas, nous obtenons:

public class Foo {
    private volatile static final Foo instance = null;

    private Foo() {
    }

    public static Foo instance() {
        if (instance != null) return instance;
        synchronized(instance) {
           Foo result = instance;
           if (instance == null) {
                result = instance = new Foo();
           return result;
        }
    }
}

Remarques:

  • Ne vous inquiétez pas pour les performances de ce code, les JVM modernes s'en occupent et c'est très bien. Après tout, optimisation prématurée est la racine de tous les maux .
  • Comme suggéré dans d'autres réponses, ce qui précède n'est pas la solution préférée de Bloch, mais je pense que l'utilisation d'une énumération pour un singleton est sémantiquement inappropriée, tout comme ce que l'OP a fait à l'origine.
0
einpoklum