vlambda博客
学习文章列表

认识23种设计模式-单例模式

单例模式,属于创建类型的一种常用的软件设计模式。单例模式在很多地方都能看到,J2EE 标准中的 ServletContext 和 ServletContextConfig、Spring 框架应用中的 ApplicationContext、数据库中的连接池等也都是单例模式。

认识23种设计模式-单例模式
认识23种设计模式-单例模式

什么是单例模式

认识23种设计模式-单例模式
认识23种设计模式-单例模式

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

认识23种设计模式-单例模式
认识23种设计模式-单例模式

单例模式的优点和缺点

认识23种设计模式-单例模式
认识23种设计模式-单例模式

单例模式的优点:

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。

  • 可以避免对资源的多重占用。

  • 单例模式设置全局访问点,可以优化和共享资源的访问。


单例模式的缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。

  • 在并发测试中,单例模式不利于代码调试。

  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

认识23种设计模式-单例模式
认识23种设计模式-单例模式

单例模式的结构

认识23种设计模式-单例模式
认识23种设计模式-单例模式


单例模式的主要角色如下。

  • 单例类:包含一个实例且能自行创建这个实例的类。

  • 访问类:使用单例的类

认识23种设计模式-单例模式

认识23种设计模式-单例模式
认识23种设计模式-单例模式

单例模式的实现

认识23种设计模式-单例模式
认识23种设计模式-单例模式


单例模式通常有两种实现形式。

第 1 种:懒汉式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:

public class LazySingleton {    // 保证 instance 在所有线程中同步    private static volatile LazySingleton instance = null; private LazySingleton() { } //private 避免类在外部被实例化 public static synchronized LazySingleton getInstance() { //getInstance 方法前加同步 if (instance == null) { instance = new LazySingleton(); } return instance; }}

注意,如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。


第 2 种:饿汉式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

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

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

认识23种设计模式-单例模式
认识23种设计模式-单例模式

单例模式在spring中的一个简单应用


spring 中加载单例的过程都是在BeanFactory接口中定义的getBean(…),实现默认是在AbstractBeanFactory中,主要代码功能两点

  • 从缓存中获取单例bean

  • 从bean的实例中获取对象

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//对传入的beanName稍作修改,防止有一些非法字段,然后提取bean 的Name final String beanName = transformedBeanName(name); Object bean;
// Eagerly check singleton cache for manually registered singletons.// 直接从缓存中获取单例工厂中的objectFactory获取单例 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } }// 返回对应的实例,从bean实例中获取对象// 有时候存在诸如BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else{ //创建bean,解决了很多关于循环依赖等等很多的功能 }

其实getBean()这个方法不仅仅和单例相关,还和prototype相关,毕竟只是获取Bean,Bean的scope也可能为prototype

/** * 单例对象的缓存 */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 首先通过名字查找这个单例bean存在不存在 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //查看缓存中是否存在这个bean实例 singletonObject = this.earlySingletonObjects.get(beanName); //如果这个时候的bean实例还是为空并且允许懒加载 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; }

上面这段代码中 synchronized (this.singletonObjects) 是关键,但是前提条件
isSingletonCurrentlyInCreation返回值也是为true,也就是这个单例Bean正在创建,所以基本上第一次调用doGetBean的时候上面这个getSingleton基本上都是返回的null,所以进行doGetBean就接着往下运行。


至此,单例模式的简单应用就介绍完了,更多设计模式内容,将会持续更新,如有不对,欢迎指教。