首先我们先来看看单例模式饿汉式中的双检锁代码,这里我们用的是菜鸟教程的资料。
代码链接:https://www.runoob.com/design-pattern/singleton-pattern.html
双检锁/双重校验锁(DCL,即 double-checked locking) |
---|
JDK 版本:JDK1.5 起 |
是否 Lazy 初始化:是 |
是否多线程安全:是 |
实现难度:较复杂 |
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。 |
实例
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
我们可以看到第二行中有个volatile 的关键字,那么volatile 有啥用呢?这时候我们要先了解在java中通过new的方式创建对象的过程的具体步骤
在通过new创建对象时有三个步骤:
- 分配内存空间
- 初始化对象
- 将内存空间的地址赋值给对应的引用
但是这个操作是非原子性操作,啥意思?就是说它不是一个整体操作,它的执行顺序是能被打乱的
可能就成了这样:
为什么会出现这种情况呢?是因为JVM在加载当前操作的时候是由指令来执行这些代码的,也就是这些代码会被翻译成一个一个的指令刚才的三步就是三个指令。指令执行要有顺序,但是jvm执行默认的时候是没有顺序的。但是这个乱序概率发生的概率非常非常的低,正常情况在是不会发生的但是要是在运行过程中电脑卡机了就可能发生cup被抢导致了正在执行的指令被抢。所以我们有个指令重排的概念,什么是指令重排?简单点刚才我们指令顺序混乱就是指令重排,而volatile的作用就是能禁止指令重排,也就是会让jvm必须按123的顺序执行。
所以不加volatile 正常情况下不会发生问题,但是仍有极小概率会出事,保险起见还是加了吧。
当然,我讲的比较浅,个人又比较菜,还在学习中,我觉得我还可以抢救一下,有错的地方多多包容一下,请帮我指正,想再深入的小伙伴可以搜索下volatile的原理