Écrivons 5 façons d'implémenter Java Singleton et divers avantages et inconvénients

Cet article est une réimpression du blog.  https://munchkins-diary.hatenablog.com/entry/2018/11/21/040358

Préface

Bonjour, je suis un développeur Java / Scala qui peut ou non être prêt à être nettoyé s'il s'agit d'une JVM après avoir appris G1GC.

Le modèle de conception appelé modèle Singleton est très célèbre, donc tout le monde le sait, mais Java a différentes façons d'écrire Singleton en utilisant des spécifications de langage autres que la manière habituelle d'écrire, donc je vais le présenter cette fois. Je vais. Au fait, je ne connaissais pas Bill Pugh avant de quitter le Japon.

En parlant du modèle Singleton, lors de l'explication des mérites, il s'agit d'un modèle de conception qui reçoit souvent une explication abstraite, comme la possibilité de créer une instance qui doit être unique en termes d'orientation d'objet.

Personnellement, je l'utilise souvent lors de la collecte d'effets secondaires lourds et forts tels que la communication avec l'extérieur et l'accès au middleware, puis l'implémentation dans une coche FlyWeight en combinaison avec une usine abstraite.

De plus, au contraire, j'aime l'utiliser lors de l'implémentation d'une implémentation plus objet sans utiliser la classe Util dans les endroits où une implémentation complètement fonctionnelle est requise.

Ce n'est pas un modèle Singleton, mais j'ai l'impression que la politique Singleton de Spring Bean est similaire à l'ancienne utilisation de Singleton. (On dirait qu'il va voler ici, alors ne le manquez pas. Si vous avez une meilleure façon de l'utiliser, veuillez laisser un commentaire!)

Puis le sujet principal!

Singleton ordinaire

Une méthode pour garantir l'unicité en interdisant les nouvelles classes externes en donnant Instance à la valeur de classe et en rendant le constructeur privé.

class NormalSingleton{
  // Singleton instance.
  private static final NormalSingleton INSTANCE = new NormalSingleton();
  // private constructor to prevent instantiation from other classes.
  private NormalSingleton() {}
  // static method to get the instance.
  public static NormalSingleton getInstance() {
    return INSTANCE;
  }
}

Comme vous le savez tous, c'est la manière la plus courante d'écrire. Cela suffit pour un cours léger. Puisque INSTANCE est un membre statique, il est chargé dans le tas lorsque la classe est chargée.

Il semble y avoir un moyen de rendre INSTANCE public, mais personnellement, je ne veux pas que les membres de la classe soient référencés directement, donc je l'appelle à partir de la méthode statique publique.

mérite

** ・ Simple et facile à mettre en œuvre ** ** ・ Facile à comprendre même pour les débutants ** ** ・ Sans fil **

Démérite

** - Ne convient pas aux classes lourdes car une instance est toujours créée lorsque la classe est chargée ** ** - Pas strictement un singleton car le constructeur est accessible par réflexion **

Utilisation

C'est une méthode d'implémentation à usage général, je vais donc la mentionner, mais si j'ose le mentionner ** · Apprentissage ** ** · Classes connues pour être fréquemment utilisées côté serveur ** ** ・ Une classe qui n'est pas lourde mais qui veut être Singleton dans un sens orienté objet ** Est-ce là?

Génération de retard

Une méthode qui n'a pas de valeur initiale, crée une instance en appelant d'abord la méthode getInstance et réutilise l'instance créée à partir de la deuxième fois.

class DelaySingleton {
  // field for singleton instance without any initial value (null)
  private static DelaySingleton instance;

  // private constructor to block the instantiation from other class.
  private DelaySingleton () {}

  // method to get the instance
  public static getInstance() {
    // create instance only in initial call of this method.
    if (instance == null) {
      instance = new DelaySingleton();
    }
    return instance;
  }
}

Il s'agit également d'un modèle de génération familier et différé. Il est souvent utilisé dans les classes légèrement plus lourdes. Il est souvent utilisé car il est facile à mettre en œuvre et le mécanisme est clair, mais comme vous pouvez le voir, ce n'est pas ThreadSafe, donc dans un environnement où le traitement parallèle est effectué, le modèle créé dans ThreadSafe à l'aide de Synchronized, qui sera présenté ensuite, est utilisé.

mérite

** ・ Simple et facile à mettre en œuvre ** ** ・ Facile à comprendre même pour les débutants ** ** - Peut retarder la génération de classes lourdes juste avant son utilisation **

Démérite

** ・ Non-thread safe ** ** ・ Parce que l'instance n'est pas finale en plus du constructeur, elle peut être réécrite par réflexion, et ce n'est plus un Singleton **

Utilisation

** · Apprentissage ** ** ・ Traitement par lots qui n'utilise pas le multithreading **

ThreadSafe avec synchronisé

Une méthode pour englober la partie où l'instance est créée avec un bloc de synchronisation et réduire fermement le nouvel appel à une seule fois.

class SynchronizedSingleton {
  // field for singleton instance without any initial value (null)
  private static SynchronizedSingleton instance;

  // private constructor to protect the instantiation from other class.
  private SynchronizedSingleton(){}

  // method to get the instance
  public static SynchronizedSingleton getInstance(){
    // check if the instance is already instantiated.
    if(instance == null){
      // block the multiple access from multiple thread
      synchronized (SynchronizedSingleton.class) {
        // check again, can be non-null, 
        // since the instance already generated just at the process just before 
        if(instance == null){
          instance = new SynchronizedSingleton();
        }
      }
    }
    return instance;
  }
}

Cela peut également être assez familier. C'est la méthode introduite dans les livres de modèles de conception et dans de nombreux articles d'introduction. Au fait, honnêtement, je n'aime pas cette façon d'écrire.

mérite

** ・ Sans fil ** ** - La génération d'instances lourdes peut être retardée juste avant d'être utilisée **

Démérite

** ・ Sato synchronisé peut entraîner une dégradation des performances ** ** ・ Parce que INSTANCE n'est pas final en plus du constructeur, il peut être réécrit par réflexion, et ce n'est plus Singleton ** ** ・ Difficile à écrire et à lire **

Utilisation

** · Apprentissage ** ** ・ Lot qui fait un usage intensif du multithreading ** ** ・ Application côté serveur exploitée par tomcat etc. ** Synchronized Singleton a beaucoup de problèmes, donc j'utilise personnellement souvent Bill Push Singleton, qui sera décrit plus tard. Bill Pugh Singleton Singleton utilisant la spécification du langage Java selon laquelle la valeur de classe de la classe interne n'est pas lue dans la mémoire avant la première référence.

class BillPushSingleton{
  // class to hold the instance as final
  private class SingletonHolder{
    // this instance won't be loaded to memory until initial reference
    // normally, class value will be loaded into memory when the class is loaded to JVM with class loader.
    private static final BillPushSingleton INSTANCE = new BillPushSingleton();
  }

  // private constructor to prevent instantiation from other classes
  private BillPushSingleton() {}

  // method to get the instance
  public static BillPushSingleton getInstance() {
    // this instance will be instantiated at the initial call only.
    return SingletonHolder.INSTANCE;   
  }
}

C'est peut-être un style d'écriture qui n'est pas très familier au Japon (pour autant que je sache), mais c'est un style d'écriture que les développeurs étrangers préfèrent utiliser. Je pense aussi que c'est simple et élégant, donc en gros j'utilise cette méthode d'écriture pour créer un Singleton de génération différée.

** Explication **

Pour le moment, la valeur de classe d'une classe normale est instanciée lorsque le chargeur de classe charge la classe dans la JVM et est conservée dans la zone permanente du tas. (Si vous ne connaissez pas la zone permanente, lisez les performances Java d'O'Reilly!) Toutefois, les constantes déclarées dans la classe interne ne sont pas nouvelles lorsque la classe parente elle-même est référencée, mais sont nouvelles et conservées dans la zone permanente uniquement lorsque la classe parente fait référence à la classe interne. Sera. Par conséquent, INSTANCE dans cette classe peut être thread-safe tout en retardant la génération jusqu'à ce que la méthode getInstance soit appelée. (En fait, il s'agit d'une spécification de chargeur de classe, et ce n'est pas un hack spécial.)

mérite

** ・ Simple et facile à mettre en œuvre ** ** ・ Sans fil ** ** - Peut retarder la génération d'instances lourdes juste avant qu'elles ne soient utilisées ** ** ・ Ne vous inquiétez pas de la dégradation des performances due à la synchronisation **

Démérite

** - Ce n'est pas strictement Singleton car il peut être instancié par réflexion ** ** ・ C'est paresseux d'expliquer aux personnes qui n'ont aucune connaissance de JVM et de chargeur de classe (la principale raison pour laquelle j'ai écrit cet article) **

Utilisation

** ・ Lot qui fait un usage intensif du multithreading ** ** ・ Application côté serveur exploitée par tomcat etc. **

Au fait, ce n'est toujours pas un Singleton strict. Tant que le constructeur reste, il ne peut pas être un singleton strict.

Enum Singleton Singleton utilisant la spécification du langage Java selon laquelle Enum est la seule instance au monde

enum EnumSingleton {
  // enum with only one element.
  // this element will act as same with instance of other impl pattern.
  INSTANCE("Now it is clear for you to see this is singleton, ahh??");

  // Constructor to make this sample code looks like class.
  // this constructor is unable to be called even from reflection as Java lang spec.
  private EnumSingleton(String dummyMessage) {
    this.dummyMessage = dummyMessage;
  }

  // dummy filed to make this sample code look like the other impl pattern
  private String dummyMessage;
  // dummy method to show this enum can work like normal class.
  public String sampleMethod() {
    return "this method is sample to show this is not just the normal enum.";
  }
}

Cette méthode de mise en œuvre peut être assez célèbre. En tant que spécification du langage Java, Enum est un singleton strict et vous ne pouvez pas créer de nouvelles instances même par réflexion. Seul Singleton implémenté en tant que enum est un vrai Singleton. Au fait, je ne pense pas que ce soit normal, donc je ne l'ai jamais utilisé simplement parce que je le connais en tant que connaissance.

mérite

** ・ Singleton strict ** ** ・ Sans fil **

Démérite

** ・ Je ne comprends pas le sens de enum car l'implémentation n'est pas bonne ** ** ・ Les interfaces et les classes abstraites ne peuvent pas être utilisées, ce qui rend difficile la combinaison avec d'autres modèles de conception ** ** ・ La génération de retard n'est pas possible ** ** ・ Je ne peux pas bien expliquer lorsqu'on me demande pourquoi je l'ai fait **

Utilisation

** ・ Lot qui fait un usage intensif du multithreading ** ** ・ Application côté serveur exploitée par tomcat etc. ** ** ・ Apaise les développeurs qui commencent à pleurer parce qu'ils ne supportent pas l'écart entre l'idéal et la réalité **

Résumé

J'ai introduit différentes manières d'implémenter le modèle Singleton que je connais. Personnellement, je pense que je ne devrais utiliser que Bill Pugh Singleton et Singleton ordinaire. Au fait, j'utilise souvent enum singleton, mais c'est un style d'écriture traditionnel, et ce n'est pas parce que je l'ai utilisé. C'est une histoire personnelle. Eh bien!

Recommended Posts

Écrivons 5 façons d'implémenter Java Singleton et divers avantages et inconvénients
Je veux implémenter diverses fonctions avec kotlin et java!
Avantages et inconvénients de Java
[Rails] Différentes manières d'écrire dans des fichiers de départ
Je veux écrire rapidement de java vers sqlite
[Java] J'ai étudié le polymorphisme, je vais donc résumer comment l'utiliser et ses mérites.
J'ai essayé d'implémenter TCP / IP + BIO avec JAVA
J'ai essayé d'implémenter la notification push Firebase en Java
[Java] Types de commentaires et comment les rédiger
Pour implémenter, écrivez le test puis codez le processus
J'ai essayé d'implémenter Sterling Sort avec Java Collector
[Java] J'ai essayé de mettre en œuvre la recherche de produits de l'API Yahoo
J'ai essayé d'implémenter la méthode de division mutuelle d'Eugrid en Java
J'ai essayé de résumer les bases de kotlin et java
Je veux faire une liste avec kotlin et java!
Je veux créer une fonction avec kotlin et java!
J'ai essayé de faire coexister Java Optional et la clause de garde
Comment écrire et noter lors de la migration de VB vers JAVA
Comment rédiger un commentaire java
[Java] Comment implémenter le multithreading
[Java] Divers résumés joints aux chefs de classe et aux membres
Je veux pouvoir penser et écrire moi-même des expressions régulières. ..
Je veux juste écrire Java en utilisant Eclipse sur mon Mac
Je veux revenir à l'écran précédent avec kotlin et java!
Bases du développement Java ~ Comment écrire des programmes (variables et types) ~
J'ai essayé de résumer les méthodes de Java String et StringBuilder