(一)单例模式

  1. 懒汉式(非线程安全)
public class SingleTonDemo {

	private static SingleTonDemo instance;
	
	private SingleTonDemo() {}
	
	public static SingleTonDemo getInstance() {
		if(instance == null) {
			instance = new SingleTonDemo();
		}
		return instance;
	}
}

这是一个很常见的单例模式,创建了一个类叫SingleTonDemo,为什么这样就能够实现单例?
(1)声明了一个私有静态的变量instance
(2)将不带参的构造函数私有化,可以防止在其他类中可以new这个类。
(3)提供一个公共获取实例的静态构造方法。为什么是静态的?因为该方法是给外部在没有实例这个类的时候可以调用
(4)判断instance是否为空,为空才实例

在不考虑并发的情况下,这种构造方式就足够了。那如果要考虑并发呢?

  1. 懒汉式(线程安全)
public class SingleTonDemo {

	private static SingleTonDemo instance;
	
	private SingleTonDemo() {}
	
	public static synchronized SingleTonDemo getInstance() {
		if(instance == null) {
			instance = new SingleTonDemo();
		}
		return instance;
	}
}

在并发的情况下,多个线程同时调用getInstance的时候,如果是第一种单例构造方式,会出现多个线程判断到instance==null,然后就会创建多个实例了,这就不是创建唯一实例了。所以当前这种构造方式就是在调用getInstance的时候加了个synchronized,将整个获取实例的方法同步。这种构造方式有弊端,当一个线程访问的时候,其他线程就会被挂起,虽然可以保证了单实例的安全性,但是也造成了很多无谓的等待

  1. 双检锁/双重校验锁
public class SingleTonDemo {

	private static SingleTonDemo instance;
	
	private SingleTonDemo() {}
	
	public static SingleTonDemo getInstance() {
		if(instance == null) {
			synchronized(SingleTonDemo.class) {
				if(instance == null) {
					instance = new SingleTonDemo();
				}
			}
		}
		return instance;
	}
}

那其实不需要每次调用getInstance都加个同步锁,只需要在生成实例的时候加上同步锁。那为什么还需要第二次判断instance是否为空呢?
假设在没有实例的情况下,a线程获取了同步锁,生成了实例。此时b线程也进来了,由于没有二次判空,还是创建一个新的实例。那是不是这种单例构造方式就最好呢?又解决了线程安全的问题,又提升了性能。其实也未必,看网上说到了jvm层面创建实例时候,有可能会造成莫名的错误。

  1. 饿汉式
public class SingleTonDemo {

	private static SingleTonDemo instance = new SingleTonDemo();
	
	private SingleTonDemo() {}
	
	public static SingleTonDemo getInstance() {
		return instance;
	}
}

这种构造方式,在类加载的时候就实例化了。我看公司项目基本上都用这种,虽然不是懒加载,可能会内存的浪费,但是感觉比较粗暴,也不容易出错。

  1. 静态内部类
public class SingleTonDemo {

	private SingleTonDemo() {}
	
	private static class SingleInner{
		private static SingleTonDemo instance = new SingleTonDemo();
	}
	
	public static SingleTonDemo getInstance() {
		return SingleInner.instance;
	}
}

这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 4 种方式不同的是:第 4 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 4 种方式就显得很合理。

上面这段话其实是引用菜鸟教程的,里面还介绍了一种枚举模式。

单例模式在我印象中只有1、2、4,其实就是懒汉式跟饿汉式,现在又多了解了双检锁跟静态内部类两种方式,也算有点收获。

文章不足之处,还真希望有读者评论一下,才能写得更好,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值