DCL-Double Check Lock
双端检锁机制
传统单机环境下的单例模式
public class Test002 {
private static Test002 instance = null;
private Test002(){
System.out.println(Thread.currentThread().getName() + " 这是一个构造器" );
}
public static Test002 getInstance(){
if(instance == null){
instance = new Test002();
}
return instance;
}
public static void main(String[] args) {
System.out.println(Test002.getInstance() == Test002.getInstance());
System.out.println(Test002.getInstance() == Test002.getInstance());
System.out.println(Test002.getInstance() == Test002.getInstance());
}
}
输出结果
多线程环境下
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Test002.getInstance();
},String.valueOf(i)).start();
}
}
输出结果
解决方法
传统可以加sync,但是sync是重锁,存在很大的弊端,直接把整个方法getInstance()加锁了
所以我们采用DCL-双端检锁机制,只给代码块加sync
public static Test002 getInstance(){
if(instance == null){
synchronized(Test002.class){
if(instance == null){
instance = new Test002();
}
}
}
return instance;
}
但是这样会出现指令重排,高并发多线程环境下,底层会对指令进行优化,导致顺序发生改变,可能会出现问题
原因是某一个线程执行到第一次检测时,读取到的instance不为null时,instance的引用对象可能还没有完成
初始化(但是内存已经被分配出去了)
instance = new Instance()可以分成以下三步:
memory = allocate();// 1.分配对象内存空间
instance(memory);// 2.初始化对象
instance = memory;// 3.设置instance指向刚刚分配的内存地址,此时instance!=null;
步骤2,3不存在数据依赖关系,所以可以进行重排优化
因此,需要加volatile修饰
private static volatile Test002 instance = null;