1. Caractéristiques du mode singleton
Privatisation constructeur
Les références de variables instanciées sont privatisées ;
La méthode d'obtention de l'instance est partagée
2. Scénario d'application en mode singleton
Une seule instance d'une classe est autorisée dans l'exécution de l'ensemble du programme ;
Objets qui doivent être instanciés puis détruits fréquemment.
Objets dont la création prend trop de temps ou de ressources, mais qui sont fréquemment utilisés.
Un environnement qui facilite la communication des ressources entre elles
3. N façons d'écrire en mode singleton en Java
C'est le moyen le plus simple, l'inconvénient est que l'objet sera instancié lors du chargement de la classe
1) Style chinois affamé
/**
* 饿汉式
*/
public class EaterSingleton {
private static final EaterSingleton INSTANCE = new EaterSingleton();
private EaterSingleton() {
}
public static EaterSingleton getInstance() {
return INSTANCE;
}
}
2) Mode paresseux - Double vérification
Thread safe, initialisation paresseuse. Cette méthode utilise un mécanisme à double verrouillage, qui est sûr et peut maintenir des performances élevées dans des situations multithread.
/**
* 懒汉双重验证
*/
public class DoubleCheckSingleton {
private volatile static DoubleCheckSingleton INSTANCE = null;
private DoubleCheckSingleton() {
}
public static DoubleCheckSingleton getInstance() {
if (INSTANCE == null) {
synchronized (DoubleCheckSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DoubleCheckSingleton();
}
}
}
return INSTANCE;
}
}
3) Classe interne statique - mode singleton
La JVM garantit un seul cas, et la classe interne ne sera pas chargée lorsque la classe externe est chargée, de sorte que le chargement paresseux peut être réalisé
package com.bytearch.designPattern.singleton;
/**
* 内部类
* 加载外部类时不会加载内部类
*/
public class AnonymousInnerClsSingleton {
private AnonymousInnerClsSingleton() {
}
private static class LazyHolder {
private final static AnonymousInnerClsSingleton INSTANCE = new AnonymousInnerClsSingleton();
}
public static AnonymousInnerClsSingleton getInstance() {
return LazyHolder.INSTANCE;
}
}
4) Enumérer le mode singleton
Il peut non seulement résoudre la synchronisation des threads, mais également empêcher la désérialisation. Le modèle de singleton d'énumération est recommandé dans "Effective Java"
package com.bytearch.designPattern.singleton;
/**
* 内部枚举类单例模式
*/
public class Singleton {
private Singleton() {
}
/**
* 静态枚举
*/
enum SingletonEnum {
INSTANCE;
private Singleton singleton;
SingletonEnum() {
singleton = new Singleton();
}
private Singleton getInstance() {
return singleton;
}
}
public static Singleton getInstance() {
return SingletonEnum.INSTANCE.getInstance();
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> {
System.out.println(Singleton.getInstance().hashCode());
}).start();
}
}
}
5. Modèle singleton de conteneur
/**
* 容器式单例模式
*/
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public static Object getBean(String className) {
Object singletonObject = singletonObjects.get(className);
if (singletonObject == null) {
synchronized (singletonObjects) {
singletonObject = singletonObjects.get(className);
if (singletonObject == null) {
try {
try {
singletonObject = Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
singletonObjects.put(className, singletonObject);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
return singletonObject;
}
}
Le modèle singleton de style conteneur est utilisé dans le framework Spring.
Jetons un coup d'œil à la source Spring org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
4. Résumé
Il existe de nombreuses "variantes" du mode singleton. Ce qui précède sont quelques-unes des meilleures implémentations résumées (et elles sont toutes thread-safe). Si vous avez un ami, vous vous demandez peut-être lequel utiliser lorsqu'il y en a autant En fait, tous peuvent être utilisés. En fonction de vos loisirs, le premier type de "style chinois affamé" peut être utilisé plus souvent au travail. Personnellement, je recommande le "mode singleton-classe interne statique",
Il est facile pour tout le monde ci-dessus de constater que le "mode singleton de style conteneur" est également une variante du "mode homme paresseux-double vérification".
Par exemple, j'utilise également le "modèle singleton de style conteneur" dans l' implémentation du pool de connexions socket léger .
public class ConnectionPool {
/**
* key is ip:port, value is ConnectionManager
*/
private final static ConcurrentHashMap<String, ConnectionManager> CP = new ConcurrentHashMap<String, ConnectionManager>();
public static Connection getConnection(InetSocketAddress socketAddress) throws MyException {
if (socketAddress == null) {
return null;
}
String key = getKey(socketAddress);
ConnectionManager connectionManager;
connectionManager = CP.get(key);
if (connectionManager == null) {
synchronized (ConnectionPool.class) {
connectionManager = CP.get(key);
if (connectionManager == null) {
connectionManager = new ConnectionManager(socketAddress);
CP.put(key, connectionManager);
}
}
}
return connectionManager.getConnection();
}
}
5. Assistance
Si vous pensez que ce contenu est très inspirant pour vous, je voudrais vous demander de faire deux petites faveurs :
Cliquez sur [Regarder/Transférer] , afin que plus de personnes puissent voir ce contenu (Si vous l'aimez ou non, cliquez sur Regarder/Transférer, c'est du hooliganisme ????)
Faites attention au compte officiel [Parlez de la structure] , l'arrière-plan du compte officiel répondra au "numéro" ou "mot clé" suivant et vous enverra un ensemble complet de supports d'apprentissage adaptés à vous.
101 : Java élémentaire
102 : Avancé Java Avancé
103 : Entretien avec Java
![](https://img-blog.csdnimg.cn/img_convert/f197d15b4ee936efad45d7f37d32a8f7.png)