设计模式之单例模式复习总结

单例模式

单例模式就是该类在整个程序的使用过程中有且仅有一个实例。也就是它的构造方法是私有的,该类负责创建自己的对象,同时确保只有一个对象被创建。当有些封装的类会去重复加载,使用单例模式可以节省资源。

单例模式有哪些?
  • 懒汉模式
  • 双重检查加锁
  • 饿汉模式
  • 静态内部类

(还有枚举,枚举的可读性差就先不看了)

懒汉模式

懒汉模式就是用到就通过调用getStance方法进行实例化。

public class Single {
	
	private static Single instance = null;
	/*
	 * 	构造方法
	 */
	private Single() {
	}
	
	public static Single getStance() {
		if (instance == null) {
			instance = new Single();
		}
		return instance;
	}
}

懒汉模式的情况会遇到一种问题,就是在多线程的情况下,在执行到if (instance == null)时时间片切换了,时间片切换到下一个线程也执行if (instance == null)由于上一个线程还没有执行完实例化对象,instance是空的所以它还会new一个对象。这样就会有多个对象,单例模式就会失效了。解决办法是加上同步锁synchronized

	public static synchronized Single getStance() {

加上同步锁之后,getStance方法就需要等一个线程执行完之后其他线程才能调用这个方法。但是又出现了新的问题同步锁会消耗很多资源,在每次调用getStance方法时,都需要执行同步锁,弊端显而易见。

饿汉模式

饿汉模式就是,这个类在创建的时候我就把我需要的东西准备好。就一开始就new出对象。

public class Single {
	
	private static Single instance = new Single();
	/*
	 * 	构造方法
	 */
	private Single() {
	}
	public static Single getStance() {
		if (instance == null) {
			instance = new Single();
		}
		return instance;
	}
}

那么从一开始就new出对象,不管几个线程,不管哪个线程执行到哪一步的时候时间片变了,里面都已经有了这个对象了。这样也会有个问题,无论什么情况都要创建,浪费了内存资源。

双重检查加锁

为了避免懒汉模式和饿汉模式所带来的问题,可以使用双重检查加锁。

public class Single {
	
	private volatile static Single instance = null;
	/*
	 * 	构造方法
	 */
	private Single() {
	}
	public static Single getStance() {
		if (instance == null) {						//第一个instance == null
			synchronized (Single.class) {
				if(instance == null) {				//第二个instance == null
					instance = new Single();
				}
			}
		}	
		return instance;
	}
}

第二次校验(instance == null),这个校验是为了防止二次创建实例。在多线程的情况下,先instance还未被实例化时,线程A进入了第一次校验,并且顺利进入到第二次校验的位置。此时资源被线程B抢占了。由于线程A还没有来得及走到new Single(),未被实例化。所以线程B可以顺利进入到new Single(),进行实例化。线程B走完了,线程A重新拿到资源往下走,第二次校验 instance == null时,instance已经被线程B实例化了,所以避免了二次创建实例。
对于双重检查加锁需要在private volatile static Single instance = null;中加入了volatile。这个关键词作用是防止指令重排。需要了解学习volatile。

静态内部类

在饿汉模式的时候提到,在创建类的时候就实例化对象,该方法会造成即便我不用也会创建,使得内存资源的浪费。这咋办嘛?那就在第一次调用的时候在创建呗,并且加上关健词final。保证内存空间地址值不会改变。只有第一次调用getInstance方法时,虚拟机才加载 Inner 并初始化instance ,只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,

方便理解:new出来的对象存放在堆当中的一个内存空间中,然后讲对象赋值给变量,变量是在栈当中生成的,那么在内存空间中使得new Single()和instance相关联的肯定不“=”,而是十六进制的地址值,对堆中的内存空间地址值赋值给变量(可以直接打印变量来查看地址值)。那么通过加上final是的第一次赋值后变量就无法改变了,地址值没法改变,所以内存空间就没法改变,保证了对象的唯一性,从而实现单例模式。

目前此方式是所有单例模式中最推荐的模式,但具体还是根据项目选择。

public class Single {
	
	private Single() {
	}
	public static Single getStance() {
		return Inner.instance;
	}
	
	private static class Inner {
		private static final Single instance = new Single();
	}
}
单例模式会被反序列化进行破坏,通过加入readResolve方法让实例唯一
private Object readResolve() throws ObjectStreamException{
        return singleton;
}
单例模式还会被反射破坏

在构造方法中加入SecurityManager(安全管理器)来解决该问题。

好记性不如烂笔头
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值