单例模式是一种常用的软件设计模式,通过它能够保证系统中,应用该模式的一个类只有一个实例。
在面试中,本人总共手写过三种单例模式:懒汉模式、饿汉模式、双重检查模式
懒汉模式:顾名思义,当系统需要类的实例的时候才会进行初始化,否则就不会进行初始化。
public class Singleton {
private static Singleton singleton = null;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
饿汉模式:饿汉就是无论系统需不需要,都会准备好该实例
public class Singleton {
private final static Singleton singleton = new Sinleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
双重检查模式:
public class Singleton {
private volatile static Singleton singleton = null;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这个代码就像进门一样,那么你先推开门看看是否可以打开,如果可以打开的话,那么你就进去,然后上锁,最后在检查一遍是否锁好。
但是本人觉得这个双重检查的重心应该实在 volatile 关键字上,如果没有加 volatile 关键字,那么在多线程的环境下就可能会调用失败。
参考:https://blog.csdn.net/xiaobudian0381/article/details/91403670
一个类进行实例化的时候简单的来说要进行三步:
- 分配内存空间
- 初始化对象
- 返回该对象的地址
在第二步和第三步中不存在依赖关系,所以可能会发生指令重排的事情,举例说明:A线程调用 getInstance() 方法,发生了指令重排(第二步 第三部 颠倒),先返回的是该对象的地址。此时,线程B调用 getInstance() 方法,发现 singleton 不是空,直接返回。问题出现了,A线程实例化的时候仅仅返回的是对象的地址,但是并没有进行初始化,但是线程B不知道,所以出现了错误。
但是加了 volatile 关键字就可以避免这种情况。关于其他的 volatile 关键字的讲解,可以参考:
https://blog.csdn.net/xiaobudian0381/article/details/90897800
https://blog.csdn.net/xiaobudian0381/article/details/91348251