//饿汉模式
public class Hunger {
//可能会浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private Hunger(){};
private final static Hunger HUNGER=new Hunger();
public static Hunger getInstance(){
return HUNGER;
}
}
//懒汉模式
public class Lazy {
/*
//单线程此处没有问题
private Lazy() {
System.out.println(Thread.currentThread().getName()+"ok");
}
private static Lazy lazy;
public Lazy getInstance(){
if (lazy == null) {
lazy = new Lazy();
}
return lazy;
}
*/
//多线程并发
private Lazy() {
System.out.println(Thread.currentThread().getName()+"ok");
}
//必须加volatile
private volatile static Lazy lazy;
//双层检测锁模式的懒汉模式(DCL懒汉模式)
public static Lazy getInstance(){
if (lazy == null) {
synchronized (Lazy.class) {//这个锁class只能有一个
if (lazy == null) {
lazy=new Lazy();//不是一个原则性操作
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
}
}
}
return lazy;
}
public static void main(String[] args) {
for(int i =0;i<=10;i++){
new Thread(()->{
Lazy.getInstance();
}).start();
}
}
}
单例模式的双重锁实现:
为什么这里要加volatile关键字呢?我们把lazy=new Lazy()这行代码进行拆分。可以分解为3个步骤:
(1)memory=allocate();// 分配内存
(2)ctorInstanc(memory) //初始化对象
(3)lazy=memory //设置s指向刚分配的地址
如果没有volatile关键字,可能会发生指令重排序。在编译器运行时,从1-2-3 排序为1-3-2。此时两个线程同时进来的时候出现可见性问题,也就是说一个线程执行了1-3,另外一个线程一进来直接返回还未执行2的null对象。而我们的volatile关键之前已经说过了,可以很好地防止指令重排序。也就不会出现这个问题了。