package com.han.demo01;
public class TestLazyMan {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan.getInstance();
}).start();
}
}
}
class LazyMan {
private LazyMan() {
//理论上这里只会打印一次
System.out.println(Thread.currentThread().getName() + "ok");
}
private static volatile LazyMan lazyMan;
/**
* 注意synchronized代码块加锁的位置,并不是包裹整个方法
* 第一个if判断的位置并还没有加锁,在某个线程进入synchronized块内部时,别的线程可以进行第一个if判断
*/
public static LazyMan getInstance() {//双重锁检查锁模式的懒汉式单例 DCL懒汉式
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
/**
* new对象不是原子操作,有三步:
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、lazyMan引用指向分配的内存空间
* 如果变量lazyMan没有加volatile关键字,那么这里可能会发生指令重排,执行顺序为1 3 2
* 如果执行顺序为1 3 2 ,那么在执行完3之后,lazyMan引用就指向了分配的内存,lazyMan不为空了
* ,但是由于这时候还没有初始化对象,所以这个lazyMan引用指向的内存中本质还是空的
* ,2还没来得及执行,但是在此时另外一个线程到达了第一个if判断,得出lazyMan不为空
* ,就会直接返回一个本质为空的对象,而得不到一个实例
* 因此,需要对lazyMan加上volatile关键字,在这里禁止指令重排
*/
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
结果:
Thread-0ok