Patron de conception Singleton
Le patron Singleton est l'un des patrons de conception les plus simples en Java. Ce type de patron de conception appartient aux patrons de création, et il fournit un moyen optimal de créer des objets.
Ce patron implique une classe unique qui est responsable de la création de son propre objet tout en garantissant qu'un seul objet est créé. Cette classe fournit un moyen d'accéder directement à son objet unique, sans avoir besoin d'instancier un objet de la classe.
Le patron Singleton est un patron de conception de création qui garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global pour accéder à cette instance.
Remarque :
- 1. Une classe Singleton ne peut avoir qu'une seule instance.
- 2. La classe Singleton doit créer sa propre instance unique.
- 3. La classe Singleton doit fournir cette instance à tous les autres objets.
summary
Patron de conception Singleton
intention
Garantir qu'une classe n'a qu'une seule instance et fournir un point d'accès global pour accéder à cette instance.
Résout principalement
Le problème de la création et de la destruction fréquentes d'instances de classes utilisées globalement.
Quand l'utiliser
Lorsqu'il est nécessaire de contrôler le nombre d'instances et d'économiser les ressources système.
Comment résoudre
Vérifiez si le singleton existe déjà dans le système ; s'il existe, renvoyez cette instance ; s'il n'existe pas, créez une nouvelle instance.
Code clé
Le constructeur est privé.
Exemple d'application
- Une classe n'a qu'un seul professeur principal.
- Lorsque Windows effectue des opérations sur des fichiers dans un environnement multi-processus et multithread, pour éviter que plusieurs processus ou threads n'opèrent simultanément sur un même fichier, cela doit être géré via une instance unique.
- Le gestionnaire de périphériques est conçu selon le modèle singleton. Par exemple, si un ordinateur possède deux imprimantes, cela évite d'imprimer simultanément le même fichier.
Avantages
- Il n'y a qu'une seule instance en mémoire, ce qui réduit la surcharge mémoire, en particulier lorsque des instances sont fréquemment créées et détruites (comme la gestion du cache des pages d'accueil d'une école de gestion).
- Éviter l'occupation multiple de ressources (telles que les opérations d'écriture de fichier).
Inconvénients
- Pas d'interface, ne peut pas être héritée.
- Cela entre en conflit avec le principe de responsabilité unique ; une classe ne devrait se préoccuper que de sa logique interne, et non de la manière dont elle est instanciée.
Cas d'utilisation
- Générer des numéros de série uniques.
- Un compteur dans le développement WEB, évitant que le compteur soit incrémenté dans la base de données à chaque rafraîchissement ; à la place, il est d'abord mis en cache.
- Créer des objets qui consomment trop de ressources, telles que les E/S et les connexions à la base de données.
Notes
- Sécurité des threads:
getInstance()Des verrous de synchronisation doivent être utilisés dans la méthode.synchronized (Singleton.class)Pour empêcher plusieurs threads d'entrer en même temps, ce qui entraînerait la création multiple de l'instance. - Initialisation paresseuse: L'instance est créée au premier appel
getInstance()à la méthode. - Sérialisation et désérialisation.: Redéfinir
readResolveMéthode pour garantir qu'aucune nouvelle instance n'est créée pendant la désérialisation. - Attaque par réflexion: Ajouter du code de garde dans le constructeur pour empêcher la création de nouvelles instances par réflexion.
- Problème de chargeur de classes: Soyez conscient des problèmes d'instances multiples qui peuvent être causés par des environnements de chargement de classes complexes.
Structure
Le patron singleton comprend les principaux rôles suivants :
- Classe singleton : une classe qui contient l'instance singleton, en déclarant généralement le constructeur comme privé.
- Variable membre statique : une variable membre statique utilisée pour stocker l'instance singleton.
- Méthode de récupération d'instance : une méthode statique utilisée pour récupérer l'instance singleton.
- Constructeur privé : empêche le code externe d'instancier directement la classe singleton.
- Gestion de la sécurité des threads : garantit que la création de l'instance singleton est sûre dans un environnement multithread.
Implémentation
Nous allons créer uneSingleObjectclasse.SingleObjectLa classe possède son constructeur privé et sa propre instance statique.
SingleObjectLa classe fournit une méthode statique pour que le monde extérieur puisse obtenir son instance statique.SingletonPatternDemoUtilisation de la classeSingleObjectclasse pour obtenirSingleObjectobjet.

Étape 1
Créer une classe Singleton.
SingleObject.java
Étape 2
Obtenir l'unique objet de la classe singleton.
SingletonPatternDemo.java
Étape 3
Exécuter le programme, résultat de sortie :
Hello World!
Plusieurs approches d'implémentation pour le patron de conception singleton
Il existe plusieurs façons d'implémenter le patron de conception singleton, comme indiqué ci-dessous :
1. Mode paresseux, non sécurisé pour les threads
Initialisation paresseuse :Oui
Est-il sécurisé pour le multithreading :non
Difficulté d'implémentation :facile
Description :C'est l'implémentation la plus basique. Le plus gros problème de cette implémentation est qu'elle ne prend pas en charge le multithreading. Comme il n'y a pas de verrou synchronisé, à proprement parler, ce n'est pas un patron singleton.
Cette approche présente un chargement paresseux évident, ne nécessite pas de sécurité des threads et ne peut pas fonctionner correctement en multithreading.
Exemple
Les différentes méthodes d'implémentation présentées ci-après prennent toutes en charge le multithreading, mais il existe des différences de performance.
2. Mode paresseux, thread-safe
Initialisation paresseuse :Oui
Est-il sûr en multithread :Oui
Difficulté d'implémentation :facile
Description :Cette approche offre un bon chargement paresseux et peut bien fonctionner en multithread, mais son efficacité est très faible, et la synchronisation n'est pas nécessaire dans 99% des cas.
Avantages : initialisation uniquement au premier appel, ce qui évite le gaspillage de mémoire.
Inconvénients : synchronized doit être ajouté pour garantir le comportement singleton, mais le verrouillage affecte l'efficacité.
Les performances de getInstance() ne sont pas critiques pour l'application (cette méthode n'est pas utilisée fréquemment).
Exemple
3. Initialisation hâtive
Initialisation paresseuse :non
Est-il sûr en multithread :Oui
Difficulté d'implémentation :facile
Description :Cette approche est relativement courante, mais elle peut facilement produire des objets inutiles.
Avantages : aucun verrouillage, l'efficacité d'exécution est améliorée.
Inconvénients : initialisé au moment du chargement de la classe, ce qui gaspille de la mémoire.
Il évite les problèmes de synchronisation multithread grâce au mécanisme de chargement de classes. Cependant, l'instance est instanciée lorsque la classe est chargée. Bien qu'il existe de nombreuses raisons qui provoquent le chargement de la classe, dans le modèle singleton, la plupart d'entre elles sont des appels à la méthode getInstance, mais il ne peut pas être confirmé qu'il n'existe pas d'autres façons (ou d'autres méthodes statiques) de provoquer le chargement de la classe. Dans ce cas, initialiser l'instance n'atteint manifestement pas l'effet du chargement paresseux.
Exemple
4. Verrouillage à double vérification / double-checked locking (DCL, c'est-à-dire double-checked locking)
Version du JDK :Depuis JDK 1.5
Initialisation paresseuse :Oui
Est-il sûr en multithread :Oui
Difficulté d'implémentation :Plus complexe
Description :Cette approche utilise un mécanisme de double verrouillage, qui est sûr et maintient de hautes performances en multithreading.
Les performances de getInstance() sont critiques pour l'application.
Exemple
5. Registre/Classe interne statique
Initialisation paresseuse :Oui
Est-il sûr en multithread :Oui
Difficulté de mise en œuvre :Moyenne
Description :Cela peut produire le même effet que le verrouillage à double vérification, mais il est plus simple à mettre en œuvre. Pour l'initialisation paresseuse de champs statiques, cette approche doit être utilisée à la place du verrouillage à double vérification. Cette approche ne s'applique qu'aux champs statiques ; le verrouillage à double vérification peut être utilisé lorsque les champs d'instance nécessitent une initialisation paresseuse.
Cette approche utilise également le mécanisme du chargeur de classes pour garantir qu'un seul thread initialise l'instance. Elle diffère de l'approche 3 en ce que : dans l'approche 3, dès que la classe Singleton est chargée, l'instance sera instanciée (sans réaliser le chargement paresseux), alors que dans cette approche, après le chargement de la classe Singleton, l'instance n'est pas nécessairement initialisée. Parce que la classe SingletonHolder n'est pas activement utilisée, c'est seulement lorsque la méthode getInstance est explicitement appelée que la classe SingletonHolder est explicitement chargée, ce qui instancie l'instance. Imaginez que si l'instanciation de l'instance consomme beaucoup de ressources, vous pouvez souhaiter qu'elle soit chargée paresseusement. D'un autre côté, vous pouvez ne pas souhaiter qu'elle soit instanciée lors du chargement de la classe Singleton, car rien ne garantit que la classe Singleton ne sera pas activement utilisée et chargée ailleurs. Dans ce cas, instancier l'instance à ce moment-là est manifestement inapproprié. À ce stade, cette approche est bien plus raisonnable que l'approche 3.
Exemple
6. Énumération
Version du JDK :Depuis JDK 1.5
Initialisation paresseuse :non
Est-il sûr en multithread :Oui
Difficulté d'implémentation :facile
Description :Cette approche d’implémentation n’a pas été largement adoptée, mais c’est la meilleure méthode pour implémenter le patron singleton. Elle est plus concise, prend automatiquement en charge le mécanisme de sérialisation et empêche absolument l’instanciation multiple.
Cette approche est préconisée par Josh Bloch, l’auteur d’Effective Java. Non seulement elle évite les problèmes de synchronisation multithread, mais elle prend aussi automatiquement en charge le mécanisme de sérialisation, empêche la désérialisation de recréer de nouveaux objets et empêche absolument l’instanciation multiple. Cependant, comme la fonctionnalité enum n’a été ajoutée qu’après JDK 1.5, cette façon d’écrire paraît inévitablement peu familière, et elle est rarement utilisée dans le travail réel.
Il ne peut pas appeler le constructeur privé via une attaque par réflexion.
Exemple
D’après l’expérience :En général, il n’est pas recommandé d’utiliser les 1re et 2e approches paresseuses ; la 3e approche hâtive est recommandée. Ce n’est que lorsqu’un chargement paresseux doit être explicitement mis en œuvre que la 5e approche d’enregistrement doit être utilisée. Si la désérialisation est impliquée dans la création d’objets, vous pouvez essayer la 6e approche enum. S’il existe d’autres exigences particulières, vous pouvez envisager la 4e approche de verrouillage à double vérification.
autres extensions