单例模式
一、什么是单例模式
java中单例模式是一种常见的设计模式,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
啥是设计模式?
设计模式好比象棋中的 “棋谱”。红方当头炮, 黑方马来跳。针对红方的一些走法, 黑方应招的时候有一些固定的套路。按照套路来走局势就不会吃亏。
软件开发中也有很多常见的 “问题场景”。针对这些问题场景, 大佬们总结出了一些固定的套路。按照这个套路来实现代码, 也不会吃亏。
二、单例模式的特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
三、单例模式的实现形式
1.理解饿汉模式、懒汉模式
以现实中吃完饭洗碗为例:
- 饿汉模式:
- 一吃完饭就把所有的碗洗干净,下次吃饭直接就可以使用洗好了的碗
- 在代码中就是提前把对象创建好,外界直接使用
- 懒汉模式:
- 吃完饭之后并不忙着洗碗,下次需要碗的时候,要用几个碗再去洗几个碗
- 在代码中并没有提前创建好对象,而是外界需要对象的时候再来创建
2.实现饿汉模式
class Singleton {
// 创建一个静态的变量,用来创建自己的对象
//注意:此处必须是private修饰,不然外界可以直接使用,每一次的调用都会产生一个对象
private static Singleton instance = new Singleton();
// 获取到实例的方法
// 外界通过这个方法来获取已经提供好了的对象
// 保证每次获取的对象都是同一个
public static Singleton getInstance() {
return instance;
}
// 禁止外部 new 实例
// 单例模式,也就只能有一个对象,设置成private,只能在类内部使用,不允许外界去创建对象
private Singleton() { }
}
饿汉模式不论是单线程还是多线程,都是线程安全的,因为饿汉模式是获取已经创建好了的对象,外界只能获取同一个对象。
测试:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//结果为true,说明获取的对象确实是一样的
System.out.println(s1==s2);
}
3.实现懒汉模式
单线程版本
class SingletonLazy {
//创建了对象,但没有new
private static SingletonLazy instance = null;
private Singleton() {}
//在方法内部,判断对象是否创建好了,如果没有再创建
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
局限:这种版本对于单线程还是可以使用的,但是运用到多线程就不行了。线程1发现对象不存在,就会去创建对象;线程2在线程1发现但还没有完全创建时也进来了,这样就会导致线程1和线程2都去创建不同的对象。
多线程版本
1.单线程版本的getInstance()不是原子操作,我们可以通过加锁来保证线程的安全性
public static SingletonLazy getInstance() {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
return instance;
}
2.观察getInstance()方法,不难发现,不论是没有创建好对象,还是已经创建好了,每次都要排队进来进行判断对象是否存在
解决办法:使用双重 if 判定, 降低锁竞争的频率,只有第一次会进来创建对象,其他时候就会直接跳过
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
- 为了避免指令重排序导致读取的 instance 出现偏差, 于是我们可以使用 volatile 来修饰instance
class SingletonLazy {
volatile private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy() { }
}
public class ThreadDemo18 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
}