设计模式-单例模式

单例模式,其实很简单,就是保持一个类仅有一个实例,并提供一个访问它的全局访问点。

简单来说,就是保证一个类在全局的范围内,只能有一个实例化的对象。一个最好的解决办法是,让这个类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且提供一个访问该实例的方法。

单例模式有什么好处呢?它可以控制客户如何去访问、何时去访问这个全局唯一的实例,也就是对唯一实例的受控访问。

单例模式有很多种写法,下面一一进行介绍

首先是饿汉式。这是最简单的一种方式。就是在类里面声明一个自身的静态对象,然后在声明时就创建它。

private static Singleton s = new Singleton();

这样的好处是,不用判断什么时候来创建这个对象,只要类被初始化,对象就创建了。但坏处就是,可能会造成资源浪费。这个对象可能并没有被使用到,但也被创建了。

然后是普通的懒汉式,它的特点是,在需要用到的时候才创建这个对象。

private static Singleton s = null;

public static Singleton getInstance() {
	if(null == s) {
		s = new Singleton();
	}
	return s;
}

懒汉式解决了资源浪费的问题,但在多线程环境下,无法保证安全性,因为多个线程可能同时访问到判断对象是否为空的代码,这个时候还是会创建多个对象。但在单线程环境下,这样做是安全的。

我们为了解决懒汉式的多线程不安全问题,可以采取加锁的方式

private static Singleton s =  null;

public synchronized static Singleton getInstance() {
	if (null ==  s) {
		s = new Singleton();
	}
	return s;
}

这样,给方法加上锁,就可以保证这个方法同时只能被一个线程访问到,就保证了线程的安全性。但问题又来了,加锁是一个比较耗费资源的操作,这样每一次访问都要进行加锁,势必会影响效率。然后我们又有了双重检查锁的写法

private volatile static Singleton s = null;

public static Singleton getInstance() {
	if(null ==  s) {
		synchronized(Singleton.class) {
			if(null ==  s) {
				s = new Singleton();
			}
		}
	}
	return s;
}

这段代码有几个比较有意思的点。首先是变量加上了volatile。这个关键字是忽略编译器的优化,保证每一次都是重新读取这个变量的值。因为编译器很可能会对一段代码进行优化,比如把值放到寄存器里面。这样如果其他线程修改了这个值,又没有及时更新寄存器,那么就会造成错误。然后是方法头部,没有了synchronized关键字,这样就避免了每一次进入这个方法都要加锁。然后是方法体,可以看到先对对象做了一次判空操作,然后加锁,在加锁块的内部,又对对象进行了一次判空操作。这是为什么呢?因为多个线程很可能同时访问到最外层的判空,如果加锁块内部不再次进行判断的话,那么即使一个线程持有了锁,创建了对象,那当这个线程释放锁,另一个线程继续执行的时候,又会执行创建对象的操作,这样就不是线程安全的了。所以双重检查锁避免了这样的问题。

最后是利用内部类来进行实现

private static class Holder {
	private static Singleton s = new Singleton();
}

public static Singleton getInstance(){
	return Holder.s;
}

这样写的好处是,它拥有了懒汉式的优点,在真正调用getInstance的时候才会加载静态内部类,而且加载时是线程安全的。

单例模式虽然简单,但如果深入研究,却也有这么多种不同的实现,嗯,好好学习。

相关demo可以参考我的gitee仓库
https://gitee.com/akitsuki-kouzou/DesignPatternDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值