采用双检锁/双重校验锁(DCL,即 double-checked locking)
引言:关于是否需要使用【volatile】的解释
/*
* 对象的创建过程
*
* 1.分配对象内存空间
* 2.初始化对象
* 3.设置instance指向刚分配的内存地址
* ==========================================
* 若发生乱序、指令重排的创建过程
*
* 1.分配对象内存空间
* 3.设置instance指向刚分配的内存地址
* 此时若其他线程调用实例对象的方法,可能会出现无法访问对象真实分配的值,因为对象的引用变量只有初始值,还没来得及赋实际值
* 2.初始化对象
*
* ==========================================
*
* volatile 如何避免上述指令重排的情况
* 第一个来的线程在锁里面加内存屏障,防止乱序,
* 坚决按照1,2,3 步骤来执行, 解决问题
*/
package single;
/**
* 懒汉模式的单例
*
* @author 苏察哈尔丶灿
*/
public class SingleCase {
// 类的唯一实例
private volatile static SingleCase instance = null;
/*
* volatile
* 第一个来的线程在锁里面忙活
*
*
* 第二个线程判断的时候,地址值已经有了,根本不会进同步锁,
* 就拿着第一个线程创建好的有地址没对象的引用去调用方法,所以报错
*
* 需要用volatile 去通知其他未进锁线程更改自己的属性状态
*/
// 构造方法私有化,阻止外部生成对象
private SingleCase() {
}
/**
* 第一种做法
* 线程不安全
*
* @return SingleCase
*/
public static SingleCase getInstance01() {
if(instance == null) {
instance = new SingleCase();
}
return instance;
}
/**
* 第二种做法
* 线程安全,被同步锁包含的不必要代码过多
* 锁等待时间稍长
*
* @return SingleCase
*/
public static synchronized SingleCase getInstance02() {
if(instance == null) {
instance = new SingleCase();
}
return instance;
}
/**
* 第三种做法
* 线程安全,但是所有线程每次进来都要等待同步锁
* 效率过差
*
* @return SingleCase
*/
public static SingleCase getInstance03() {
synchronized(SingleCase.class) {
if(instance == null) {
instance = new SingleCase();
}
}
return instance;
}
/**
* 第四种做法
* 线程安全,避免不必要的锁等待
* 效率提升
*
* 双检锁/双重校验锁(DCL,即 double-checked locking)
*
* @return SingleCase
*/
public static SingleCase getInstance04() {
if(instance == null) {
synchronized(SingleCase.class) {
if(instance == null) {
instance = new SingleCase();
}
}
}
return instance;
}
}