创建型模式中的单例模式(Singleton)

前言

这里可以添加本文要记录的大概内容:
讲解了创建型模式中的单例模式,包括单例模式在面试中常会问道的一个面试题以及它在Spring中是如何运用到的

文章不足,欢迎补充....


提示:以下是本篇文章正文内容,下面内容可供参考

一、单例模式的介绍

特点:对一些重量级的对象,省略了重复创建对象花费的时间,减少了系统的开销,第二点是使用单例可以减少new操作的次数,减少了GC线程回收内存的压力

对于单例bean的创建方式,主要看DefaultSingletonBeanRegistry 的 getSingleton() 方法:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    /** 保存单例Objects的缓存集合ConcurrentHashMap,key:beanName --> value:bean实例 */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            //检查缓存中是否有实例,如果缓存中有实例,直接返回
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //省略...
                try {
                    //通过singletonFactory获取单例
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                //省略...
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            //返回实例
            return singletonObject;
        }
    }
    
    protected void addSingleton(String beanName, Object singletonObject) {
      synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
      }
    }

}

从源码中可以看出,是通过ConcurrentHashMap的方式,如果在Map中存在则直接返回,如果不存在则创建,并且put进Map集合中,并且整段逻辑是使用同步代码块包住的,所以是线程安全的。

能减少我们内存空间,节约性能使得性能提升:

以连接数据库工具类DBAccess 为例:

 工具类建立一个静态的对象,放到常量区

二、懒汉模式与饿汉模式的区别

面试题:当我们和面试官聊到单例模式,面试官常会问道懒汉模式与饿汉模式的区别?

分类:
  - 饿汉式(静态常量)
  - 饿汉式(静态代码块)
  - 懒汉式(线程不安全)
  - 懒汉式(线程安全,同步代码块)
  - 懒汉式(线程安全,同步方法)
  - 双重检查
  - 静态内部类
  - 枚举

懒汉模式:当我们真正需要使用对象时才去创建该单例类对象

/**
 * 双重检查
 */
class DBAccess6 {
    private DBAccess6() {

    }

    private static DBAccess6 dbAccess = null;

    public static DBAccess6 getInstance() {
        if (dbAccess == null) {
            synchronized (DBAccess6.class) {
                if (dbAccess == null) {
                    dbAccess = new DBAccess6();
                }
            }
        }
        return dbAccess;
//        return new DBAccess6();
    }
}




/**
 * 静态内部类
 */
class DBAccess7 {
    private DBAccess7() {

    }

    private static class DBAccess7Instance{
        private static DBAccess7 dbAccess = new DBAccess7();
    }

    public static DBAccess7 getInstance() {
        return DBAccess7Instance.dbAccess;
    }
}



/**
 * 枚举
 */
enum DBAccess8{
    DBACCESS;
    public static DBAccess8 getInstance() {
        return DBAccess8.DBACCESS;
    }
}

饿汉模式:在类加载时已经创建好该单例对象,等待被程序使用(巴不得别人创建好,我们直接拿过来使用)

/**
 *
 * 饿汉式(静态常量)
 */
public class DBAccess {
    //    构造器私有化,避免外部创建对象
    private DBAccess() {

    }

    //    static修饰,保障其能够被静态方法访问
    private final static DBAccess dbAccess = new DBAccess();

    //    外部直接调用静态方法实例化对象
    public static DBAccess getInstance() {
        return dbAccess;
    }

}

/**
 * 饿汉式(静态代码块)
 */
class DBAccess2 {
    private DBAccess2() {

    }

    private static DBAccess2 dbAccess = null;

    static {
        dbAccess = new DBAccess2();
    }

    public static DBAccess2 getInstance() {
        return dbAccess;
    }
}

结论:

        单例中两种饿汉式可用,但是存在性能问题

​        单例中三种懒汉式不推荐,存在线程安全问题,同步方法的方式解决了线程的问题,但是性能极差

        单例模式推荐用静态内部类、枚举、双重检查这三种

应用:
   jdk源码中Runtime类
   tomcat中ApplicationContext类
   session 工厂

三、单例模式在Spring中的应用 

Spring中的JavaBean默认为单例的,bean 可以被定义为两种模式:prototype(多例)和 singleton(单例)。

  • singleton(单例):只有一个共享的实例存在,所有对这个 bean 的请求都会返回唯一的实例。
  • prototype(多例):对这个 bean 的每次请求都会创建一个新的 bean 实例,类似于 new。

因为SpringContext中会有很多个dao\service\action对象;如果Bean为多例的话,就每次要用到的时候,都会重新去创建一个新的对象,内存中就会有很多重复的对象

配置文件如下:

<bean id="singleton" class="java.util.Date" scope="singleton"></bean>
<bean id="prototype" class="java.util.Date" scope="prototype"></bean>

单例模式的弊端:容易产生变量污染

总结

以上就是今天要讲的内容,本文仅仅简单介绍了单例模式以及单例模式在Spring中的体现


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值