最优雅的单例设计模式

1.单例设计模式是什么

单例设计模式就是一个类,只生成一个对象 ,给予其他类调用。

所以至少至少要保证三点:

  1. 构造器私有化。

  2. 类本身创建对象。

  3. 提供获取对象的方法

2.常见的几种单例创建方式

2.1 饿汉式

在类加载到内存,就实例化一个实例。

jvm保证线程安全简单实用,简单以用,线程安全。

缺点:不管是否用到,类加载时完成实例化了。有人说这个会影响程序启动时间,影响很小。

public class Singleton01 {
    // static 变量,在类加载时,初始化变量
    private static final Singleton01 instance = new Singleton01();

    //私有构造器,让别人不能new
    private Singleton01(){}

    public static Singleton01 getInstance(){
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton01.getInstance()==Singleton01.getInstance());
    }
}

2.2 懒汉式 lazy loading(双重检查)

实现按需加载,同时带来了线程不安全的问题,。

解决线程安全(同时保证效率):双重检查(volatile synchronized),减小加锁范围synchronized,提高效率,同时线程安全。

缺点:代码变复杂了。

public class Singleton06 {
    //双重检查volatile防止指令重排
    private static volatile Singleton06 instance;

    //私有构造器,让别人不能new
    private Singleton06(){}
    //双重检查实现线程安全、效率较高的单例模式
    public static Singleton06 getInstance(){
        if(instance == null){
            synchronized(Singleton06.class){
                if(instance == null) {
                    instance = new Singleton06();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        System.out.println(Singleton06.getInstance()== Singleton06.getInstance());
    }
}

2.3 静态内部类

jvm保证单例, 加载外部类时不会加载内部类,这样就实现了懒加载,同时也保证了线程安全。

public class Singleton07 {

    //私有构造器,让别人不能new
    private Singleton07(){}

    //静态内部类,初始化 new Singleton07();
    //jvm只加载一次类,同时初始化静态变量
    private static class Singleton07Holder{
        private final static Singleton07 instance = new Singleton07();
    }

    public static  Singleton07 getInstance(){
        return Singleton07Holder.instance;
    }
    public static void main(String[] args) {
        System.out.println(Singleton07.getInstance()== Singleton07.getInstance());
    }
}

2.4 枚举实现

据说这种是最完美的一种。枚举解决线程同步,以及反序列化问题。

public enum  Singleton08 {
    INSTANCE;

    public static void main(String[] args) {
        System.out.println(Singleton08.INSTANCE== Singleton08.INSTANCE);
    }
}

个人觉得:这四种都可以使用,推荐静态内部类实现,但是没有最好的,按需即可。

3.应用场景

只需要一个实例,秉承着多new一个实例,多浪费内存空间。

比如各种Manager、各种Factory。

我们开发中使用最多的是spring中ioc容器中的单实例bean的实例化。

在实例化对象之前,会锁住singletonObjects map对象,具体如下。

synchronized (this.singletonObjects) {}

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		//锁定singletonObjects对象
		synchronized (this.singletonObjects) {
			//从缓存中获取单例对象
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				//单例创建前的检查,并将其放入singletonsCurrentlyInCreation中
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					/**
					 * 核心: 从{@link org.springframework.beans.factory.ObjectFactory#getObject()}加载bean
					 */
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					} 
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					//单例创建后的检查,并将其从singletonsCurrentlyInCreation中移除
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

小小总结: 在开发过程中,只要不涉及自己重新搭建架构,基本上已经很少需要自己实例化单例了。spring已经帮我们完成了单实例的创建了。

面试必问设计模式,尽量从spring源码中重新整理一遍23种设计模式。有兴趣的同学可以看看我的源码分析系列,目前正在分析spring源码1、spring源码解析之概况流程

未完待续。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值