Modèle Singleton : est un modèle de conception de logiciel couramment utilisé. Dans sa structure de base, il contient une classe spéciale appelée singleton. Une classe n’a qu’une seule instance, c’est-à-dire qu’une classe n’a qu’une seule instance d’objet.
Pour certaines classes du système, une seule instance est importante. Par exemple, il peut y avoir plusieurs tâches d'impression dans un système, mais il ne peut y avoir qu'une seule tâche de travail lors de la vente de billets, il y en a 100 au total ; Pour un billet, plusieurs guichets peuvent vendre des billets en même temps, mais il faut s'assurer qu'il n'y a pas de surréservation (le nombre de billets restant ici est une seule instance et la vente de billets implique plusieurs threads). Si un mécanisme n'est pas utilisé pour unifier l'objet fenêtre, plusieurs fenêtres apparaîtront. Si ces fenêtres affichent toutes le même contenu, une création répétée gaspillera des ressources.
Scénario d'application (Source : "Dahua Design Pattern") :
Exigence : Créer une fenêtre de boîte à outils sur le front-end La boîte à outils n'apparaîtra pas ou une seule. apparaîtra.
Problème rencontré : La fenêtre "Boîte à outils" sera créée à plusieurs reprises à chaque fois que vous cliquerez sur le menu.
Solution 1 : utilisez l'instruction if pour déterminer d'abord si elle est nulle à chaque fois que vous créez un objet. S'il est nul, créez à nouveau l'objet.
Exigences : Si vous devez instancier le formulaire boîte à outils à 5 endroits
Problèmes rencontrés : Ce petit bug doit être modifié à 5 endroits, et le code est répété, et le taux d'utilisation du code est faible
Solution 2 : utilisez le modèle singleton pour vous assurer qu'une classe n'a qu'une seule instance et fournissez un point d'accès global pour y accéder.
Le mode Singleton paresseux peut être divisé en style paresseux et style affamé :
Mode singleton paresseux : ne s'initialise pas lorsque la classe est chargée.
Mode singleton de style affamé : l'initialisation est terminée lorsque la classe est chargée, donc le chargement de la classe est lent, mais la vitesse d'acquisition des objets est rapide.
Le premier type (fainéant, fil de discussion dangereux) :
public class SingletonDemo1 {private static SingletonDemo1 instance;private SingletonDemo1(){}public static SingletonDemo1 getInstance(){if (instance == null) { instance = new SingletonDemo1(); }return instance; } }
Cette façon d'écrire est très chargement paresseux Évidemment, mais fatalement, cela ne fonctionne pas correctement avec plusieurs threads.
Le deuxième type (paresseux, thread-safe) :
public class SingletonDemo2 {private static SingletonDemo2 instance;private SingletonDemo2(){}public static synchronized SingletonDemo2 getInstance(){if (instance == null) { instance = new SingletonDemo2(); }return instance; } }
Cette façon d'écrire est dans getInstance() Un verrou synchronisé est ajouté à la méthode. Cela fonctionne bien dans plusieurs threads et semble avoir un bon chargement paresseux, mais il est très inefficace (à cause des verrous) et ne nécessite pas de synchronisation dans la plupart des cas.
Le troisième type (homme affamé) :
public class SingletonDemo3 {private static SingletonDemo3 instance = new SingletonDemo3();private SingletonDemo3(){}public static SingletonDemo3 getInstance(){return instance; } }
Cette méthode évite de nombreux problèmes en fonction du classloder Problème de synchronisation des threads, cependant, l'instance est instanciée lorsque la classe est chargée. À ce stade, l'initialisation de l'instance n'obtient évidemment pas l'effet d'un chargement paresseux.
Le quatrième type (FaimHan, variante) :
public class SingletonDemo4 {private static SingletonDemo4 instance = null;static{ instance = new SingletonDemo4(); }private SingletonDemo4(){}public static SingletonDemo4 getInstance(){return instance; } }
Surface Cela semble être assez différent, mais en fait la troisième voie est presque la même, les deux sont instanciées dans l'instance d'initialisation de classe
La cinquième voie (classe interne statique) :
public class SingletonDemo5 {private static class SingletonHolder{private static final SingletonDemo5 instance = new SingletonDemo5(); }private SingletonDemo5(){}public static final SingletonDemo5 getInsatance(){return SingletonHolder.instance; } }
Cette méthode utilise également le mécanisme classloder pour garantir qu'il n'y a qu'un seul thread lors de l'initialisation de l'instance. Il est similaire au troisième. et la quatrième méthode. La différence est (différence très subtile) : les troisième et quatrième méthodes sont que tant que la classe Singleton est chargée, l'instance sera instanciée (l'effet de chargement paresseux n'est pas obtenu), et cette méthode est que la classe Singleton est instanciée. La classe Singleton est chargée Oui, l'instance n'est pas nécessairement initialisée. Étant donné que la classe SingletonHolder n'est pas activement utilisée, la classe SingletonHolder ne sera explicitement chargée qu'en appelant la méthode getInstance pour instancier l'instance. Imaginez si l'instanciation d'une instance consomme des ressources et que je souhaite qu'elle soit chargée paresseusement. D'un autre côté, je ne souhaite pas l'instancier lorsque la classe Singleton est chargée, car je ne peux pas garantir que la classe Singleton puisse être utilisée activement dans d'autres. places est chargé, alors il est évidemment inapproprié d'instancier l'instance pour le moment. À l’heure actuelle, cette méthode semble plus raisonnable que les troisième et quatrième méthodes.
Sixième (énumération) :
public enum SingletonDemo6 { instance;public void whateverMethod(){ } }
Cette méthode est Josh Bloch, auteur de Effective Java La préconisée La méthode peut non seulement éviter les problèmes de synchronisation multi-thread, mais également empêcher la désérialisation de recréer de nouveaux objets. Cependant, je pense personnellement que puisque la fonctionnalité d'énumération n'a été ajoutée que dans la version 1.5, en utilisant. cette méthode L'écriture donne inévitablement l'impression que les gens ne sont pas familiers. Dans le travail réel, je vois rarement quelqu'un écrire comme ça.
Septième type (serrure à double contrôle) :
public class SingletonDemo7 {private volatile static SingletonDemo7 singletonDemo7;private SingletonDemo7(){}public static SingletonDemo7 getSingletonDemo7(){if (singletonDemo7 == null) {synchronized (SingletonDemo7.class) {if (singletonDemo7 == null) { singletonDemo7 = new SingletonDemo7(); } } }return singletonDemo7; } }
Il s'agit d'une version améliorée de la deuxième méthode, communément appelée verrouillage à double vérification. Pour une introduction détaillée, veuillez consulter :
Après JDK1.5, le verrouillage à double vérification peut obtenir l'effet singleton normalement.
Instance unique en multi-threading Dans un programme multi-thread, si plusieurs threads accèdent à une seule instance en même temps, il est possible d'en créer plusieurs. cas. A ce moment, vous devez le verrouiller avec un « cadenas ». Y compris les verrous, les blocages et la communication entre les verrous. Cette partie sur le multi-threading sera expliquée en détail plus tard !
Référence :
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!