Spring:单例模式知识记录

目录

 传统的创建类代码

单例类创建

(1)饿汉模式(以空间换时间,可能存在资源消耗问题)

(2)懒汉模式(可能存在线程不安全问题)

(3)方法上添加synchronized锁的懒汉模式(造成系统瓶颈)

(3)添加双重校验锁的懒汉模式(JVM指令重排可能导致程序出错)

(4)添加双重检验锁与volatile关键字的懒汉模式

(5)静态内部类的单例模式(推荐的写法)

Spring单例模式源码


单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

 传统的创建类代码

/**
 * @author:py
 * @date:
 * @description:传统创建类实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
        Singleton singleton1 = new Singleton();
        Singleton singleton2 = new Singleton();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类
 * @version:
 */

class Singleton {
    
}

每次new Singleton()就会创建一个Singleton实例,并不符合单例类只有一个实例的要求。

单例类创建

(1)饿汉模式(以空间换时间,可能存在资源消耗问题)

Step 1. 构造函数私有化。

Step 2. 自行对外提供实例。

Step 3. 提供外界可以获得该实例的方法。

/**
 * @author:py
 * @date:
 * @description:单例模式实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
//        Singleton singleton1 = new Singleton();
        //单例
        Singleton singleton2 = Singleton.getInstance();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类(饿汉模式)
 * @version:
 */

class Singleton {
    // step 2.自行对外提供实例
    private static Singleton singleton = new Singleton();
    
    //step 1.构造函数私有化
    private Singleton(){}
    
    //step 3.提供外界可以获得该实例的方法
    public static Singleton getInstance() {
        return singleton;
    }
}

(2)懒汉模式(可能存在线程不安全问题)

如果创建单例对象会消耗大量资源,可以用懒汉模式延迟创建对象。但是在多线程并发的情况下使用懒汉模式,会并发调用getInstance()方法,导致系统同时创建多个单例类实例。

/**
 * @author:py
 * @date:
 * @description:单例模式实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
//        Singleton singleton1 = new Singleton();
        //单例
        Singleton singleton2 = Singleton.getInstance();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类(懒汉模式)
 * @version:
 */

class Singleton {
    // step 2.自行对外提供实例
    private static Singleton singleton = null;

    //step 1.构造函数私有化
    private Singleton(){}

    //step 3.提供外界可以获得该实例的方法
    public static Singleton getInstance() {
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

(3)方法上添加synchronized锁的懒汉模式(造成系统瓶颈)

添加synchronized锁可以保证线程安全,但在每次访问getInstance()方法时都会进行加锁和解锁的操作,同时synchronized锁添加在方法上,作用范围过大,而单例类是全局唯一的,锁的操作会成为系统的瓶颈。

/**
 * @author:py
 * @date:
 * @description:单例模式实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
//        Singleton singleton1 = new Singleton();
        //单例
        Singleton singleton2 = Singleton.getInstance();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类(懒汉模式+synchronized锁)
 * @version:
 */

class Singleton {
    // step 2.自行对外提供实例
    private static Singleton singleton = null;

    //step 1.构造函数私有化
    private Singleton(){}

    //step 3.提供外界可以获得该实例的方法
    public static synchronized Singleton getInstance() {
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

(3)添加双重校验锁的懒汉模式(JVM指令重排可能导致程序出错)

/**
 * @author:py
 * @date:
 * @description:单例模式实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
//        Singleton singleton1 = new Singleton();
        //单例
        Singleton singleton2 = Singleton.getInstance();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类(懒汉模式+双重校验锁)
 * @version:
 */

class Singleton {
    // step 2.自行对外提供实例
    private static Singleton singleton = null;

    //step 1.构造函数私有化
    private Singleton(){}

    //step 3.提供外界可以获得该实例的方法
    public static Singleton getInstance() {

        //第一次校验
        if(singleton == null){
            synchronized (Singleton.class){
                //第二次校验
                if(singleton == null){
                    //创建对象,非原子操作
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

上述代码中,singleton = new Singleton()指令实际可抽象为以下几条JVM指令:

//Step 1. 分配对象的内存空间
memory = allocate();

//Step 2. 初始化对象
ctorInstance(memory);

//Step 3. 设置instance指向刚分配的内存地址
singleton = memory;

其中,操作2依赖操作1,但操作3不依赖操作2,所以JVM会针对他们进行指令优化重排序。

指令重排:是指JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。

JVM指令重排序后如下所示:

//Step 1. 分配对象的内存空间
memory = allocate();

//Step 3. 设置instance指向刚分配的内存地址,此时对象还未初始化
singleton = memory;

//Step 2. 初始化对象
ctorInstance(memory);

如果线程A正在执行这段赋值语句,在初始化分配对象之前就将其赋值给singleton引用,恰好此时线程B进入该方法判断singleton引用不为null,然后将其返回使用,就会导致程序出错。

(4)添加双重检验锁与volatile关键字的懒汉模式

使用volatile关键字修饰singleton字段可以禁止指令的重排序优化,阻止JVM对其相关代码进行指令重排,从而保证按既定的顺序指令执行。

/**
 * @author:py
 * @date:
 * @description:单例模式实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
//        Singleton singleton1 = new Singleton();
        //单例
        Singleton singleton2 = Singleton.getInstance();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类(懒汉模式+双重校验锁+volatile关键字)
 * @version:
 */

class Singleton {
    // step 2.自行对外提供实例
    private static volatile Singleton singleton = null;

    //step 1.构造函数私有化
    private Singleton(){}

    //step 3.提供外界可以获得该实例的方法
    public static Singleton getInstance() {

        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

(5)静态内部类的单例模式(推荐的写法)

/**
 * @author:py
 * @date:
 * @description:单例模式实例
 * @version:
 */

public class Case_1 {

    public static void main(String[] args) {
//        Singleton singleton1 = new Singleton();
        //单例
        __Singleton singleton2 = __Singleton.getInstance();
    }
}

/**
 * @author:py
 * @date:
 * @description:单例类(静态内部类)
 * @version:
 */

class __Singleton {
    //step 2.私有的静态内部类,类加载器负责加锁
    private static class SingletonHolder{
        private static __Singleton singleton = new __Singleton();
    }

    //step 1.构造函数私有化
    private __Singleton(){}

    //step 3.提供外界可以获得该实例的方法
    public static __Singleton getInstance() {
        return SingletonHolder.singleton;
    }
}

Spring单例模式源码

Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean()方法里。getBean()方法内部调用doGetBean()方法,doGetBean()方法内部调用父类FactoryBeanRegistrySupport的父类DefaultSingletonBeanRegistry的getSingleton()方法进行Bean的创建。非lazy-init方式,在容器初始化时进行调用,lazy-init方式,在用户向容器第一次索要bean时进行调用。

getBean()、doGetBean()和getSingleton()方法的源码具体如下:

    public Object getBean(String name) throws BeansException {
        return this.doGetBean(name, (Class)null, (Object[])null, false);
    }        

    protected  T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        Object sharedInstance = this.getSingleton(beanName);
        Object bean;

        //省略部分代码

    }


    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            Map var4 = this.singletonObjects;
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

        return singletonObject;
    }

从getSingleton()方法中可以看到,Spring进行依赖注入时,使用了双重校验锁的单例模式。

先从缓存singletonObjects(实际是一个ConcurrentHashMap)中获取bean实例。

Spring源码DefaultSingletonBeanRegistry类中对singletonObjects进行了如下定义:

private final Map singletonObjects = new ConcurrentHashMap(256);

如果bean实例为null,对缓存singletonObjects加锁,然后再从缓存earlySingletonObjects(实际是个HashMap)中获取bean实例,如果继续为null,就创建一个bean。

Spring源码DefaultSingletonBeanRegistry类中对earlySingletonObjects进行了如下定义:

private final Map earlySingletonObjects = new HashMap(16);

与前面所述的单例模式使用私有构造方法创建bean不同点在于,这里是通过singletonFactory.getObject()来返回具体的beanName对应的ObjectFactory来创建bean。实际上是调用了AbstractAutowireCapableBeanFactory的doCreateBean()方法,返回了BeanWrapper包装并创建的bean实例。

doCreateBean()方法部分代码如下:

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }

        if (instanceWrapper == null) {
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }

        Object bean = instanceWrapper.getWrappedInstance();
        Class beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        Object var7 = mbd.postProcessingLock;
        synchronized(mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                } catch (Throwable var17) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);
                }

                mbd.postProcessed = true;
            }
        }

        boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
        if (earlySingletonExposure) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
            }

            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }
    
        //省略部分代码
    }

 

你可能感兴趣的:(JavaWeb开发,java,设计模式,spring)