关于单例模式可查看博客 java 单例模式的5种实现方式
我们直接上代码, 看看不加volatile会出什么问题?
public class Friday{
private static Friday friday;
private Friday() {}
public static Friday getInstance() {
if (friday == null) {
synchronized(Friday.class) {
if(friday == null) {
friday = new Friday(); //这行代码是问题根源所在
}
}
}
return friday;
}
}
为什么那行代码有问题呢?
因为friday = new Friday();这段代码并非原子操作,可以分为3步骤,伪代码如下:
memory = allocate();//1:分配对象的内存空间
initInstance(memory)//2:在内存空间内初始化对象
instance = memory;//3:将instance指向刚分配的内存空间
其中第2步与第3步之间可能会发生指令重排序:也就是变成
memory = allocate();//1:分配对象的内存空间
instance = memory;//3:将instance指向刚分配的内存空间(此时还没有初始化对象)
initInstance(memory)//2:在内存空间内初始化对象
重排序之后,在单线程中并不会影响语义,但是多线程就不一定了。
时间段 | 线程A | 线程B |
---|---|---|
t0 | A1:memory = allocate();//1分配对象的内存空间 | |
t1 | A3:instance = memory;//3:将instance指向刚分配的内存空间(此时还没有初始化对象) | |
t2 | B1:判断instance是否为空 | |
t3 | B2:由于instance不为null,线程B将返回instance引用的对象(而这个时候对象还没有初始化) | |
t4 | A2:initInstance(memory)//在内存空间内初始化对象 | |
t5 | A4:返回instance引用的对象 |
可以看出, 线程B拿到的instance可能还未初始化成功,如果拿去操作,就出问题了。
解决方法是在变量声明时加上volatile修饰符, 如:private static volatile Friday friday;
这是因为jdk1.5对volatile进行优化, 有一个作用是禁止指令重排序。
网友评论