Modèle d’instanciation singleton thread-safe paresseux en java

L’instantané singleton thread-safe paresseux n’est pas facile à comprendre pour chaque codeur, alors je voulais créer une classe dans notre framework d’entreprise qui ferait le travail.

Qu’est-ce que tu en penses? Voyez-vous quelque chose de mauvais à ce sujet? Y a-t-il quelque chose de similaire dans Apache Commons? Comment puis-je le rendre meilleur?

Supplier.java

public interface Supplier { public T get(); } 

LazyThreadSafeInstantiator.java

 public class LazyThreadSafeInstantiator implements Supplier { private final Supplier instanceSupplier; private volatile T obj; public LazyThreadSafeInstantiator(Supplier instanceSupplier) { this.instanceSupplier = instanceSupplier; } @Override // http://en.wikipedia.org/wiki/Double-checked_locking public T get() { T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won't hurt. if (result == null) { synchronized(this) { result = obj; if (result == null) { result = instanceSupplier.get(); obj = result; } } } return result; } } 

Exemple d’utilisation:

 public class Singleton1 { private static final Supplier instanceHolder = new LazyThreadSafeInstantiator(new Supplier() { @Override public Singleton1 get() { return new Singleton1(); } }); public Singleton1 instance() { return instanceHolder.get(); } private Singleton1() { System.out.println("Singleton1 instantiated"); } } 

Merci

    l’instion singleton de thread-safe paresseux est un peu difficile à comprendre pour chaque codeur

    Non, c’est en fait très, très facile:

     public class Singleton{ private final static Singleton instance = new Singleton(); private Singleton(){ ... } public static Singleton getInstance(){ return instance; } } 

    Mieux encore, faites-en un enum:

     public enum Singleton{ INSTANCE; private Singleton(){ ... } } 

    C’est threadsafe, et c’est paresseux (l’initialisation se produit au moment du chargement de la classe, et Java ne charge pas les classes jusqu’à ce qu’elles soient référencées pour la première fois).

    Le fait est que 99% du temps, vous n’avez pas du tout besoin de chargement paresseux . Et sur le 1% restant, à 0,9%, ce qui précède est suffisamment paresseux.

    Avez-vous lancé un profileur et déterminé que votre application correspond au 0,01% qui nécessite vraiment un chargement paresseux au premier access? Je ne le pensais pas. Alors pourquoi perdez-vous votre temps à concocter ces abominations du code Rube Goldbergesque pour résoudre un problème non existant?

    Pour une version plus lisible (à mon avis) que celle présentée dans la question, on peut se référer à l’ idiome Initialization on Demand Holder , introduit par Bill Pugh. Non seulement le thread-safe est-il compatible avec le modèle de mémoire Java 5, mais le singleton est également initialisé paresseusement.

    Cela me semble trop ingénieux.

    Je ne vois vraiment pas en quoi le fait d’avoir des cours d’ assistance peut aider .

    Tout d’abord, il utilise un idiome à double locking , et il a été prouvé une fois de plus qu’il était cassé.

    Deuxièmement, si vous devez utiliser singleton, pourquoi ne pas initialiser static final instance static final .

     public class Singleton1 { private static final Singleton1 instanceHolder = new Singletong1( ); public Singleton1 instance() { return instanceHolder; } private Singleton1() { System.out.println("Singleton1 instantiated"); } } 

    Ce code est compatible avec les threads et a fait ses preuves.

    Vérifiez la réponse de Vineet Reynolds lorsque vous devez initialiser l’instance singleton sur un premier get . Dans de nombreux cas, je pense que cette approche est également excessive.

    Le schéma de locking à double vérification et l’utilisation de composants volatils sur les compilateurs JIT et les systèmes multi-core / processeurs ne sont-ils pas dus au modèle de mémoire Java et à la possibilité d’une exécution en dérangement?

    Plus généralement, il semblerait qu’un cadre pour les singletons soit excessif pour ce qui est essentiellement un modèle assez simple à mettre en œuvre correctement.

    Je suis d’accord avec les autres affiches et je dis que cela semble exagéré, mais j’ai dit que je pense que c’est une chose qu’un développeur junior risque de se tromper. Étant donné que le comportement du fournisseur qui construit le singleton (illustré ci-dessous) sera le même dans presque tous les cas, je serais tenté de le LazyThreadSafeInstantiator comme comportement par défaut dans LazyThreadSafeInstantiator . L’utilisation de la classe interne chaque fois que vous souhaitez utiliser un singleton est vraiment compliquée.

      @Override public Singleton1 get() { return new Singleton1(); } 

    Cela pourrait être fait en fournissant un constructeur surchargé qui prend la classe au singleton requirejs.

     public class LazyThreadSafeInstantiator implements Supplier { private final Supplier instanceSupplier; private Class toConstruct; private volatile T obj; public LazyThreadSafeInstantiator(Supplier instanceSupplier) { this.instanceSupplier = instanceSupplier; } public LazyThreadSafeInstantiator(Class toConstruct) { this.toConstruct = toConstruct; } @Override // http://en.wikipedia.org/wiki/Double-checked_locking public T get() { T result = obj; // Wikipedia: Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others, it won't hurt. if (result == null) { synchronized(this) { result = obj; if (result == null) { if (instanceSupplier == null) { try { Constructor[] c = toConstruct.getDeclaredConstructors(); c[0].setAccessible(true); result = c[0].newInstance(new Object[] {}); } catch (Exception e) { //handle } result = } else { result = instanceSupplier.get(); } obj = result; } } } return result; } } 

    Ce serait alors utilisé comme tel.

     private static final Supplier instanceHolder = new LazyThreadSafeInstantiator(Singleton1.getClass()); 

    Ceci est mon avis est un peu plus propre. Vous pouvez également étendre cela davantage en utilisant des arguments de constructeur.

     Lazy lazyX= new Lazy(){ protected X create(){ return new X(); }}; X x = lazyX.get(); 

     abstract public class Lazy { abstract protected T create(); static class FinalRef { final S value; FinalRef(S value){ this.value =value; } } FinalRef ref = null; public T get() { FinalRef result = ref; if(result==null) { synchronized(this) { if(ref==null) ref = new FinalRef( create() ); result = ref; } } return result.value; } } 

    sauf peut-être le premier get () dans un thread, tous les appels get () ne nécessitent aucune synchronisation ou lecture volatile. l’objective initial de double vérification du locking est atteint.