在单例模式中,有些对象的初始化开销比较大,程序设计时可采用的技巧是延迟初始化,即将该对象的初始化放在函数里,需要的时候再调用该函数对该对象进行初始化。
正确的双检锁延迟初始化代码如下:
package cn.itcast.day06.demo6;
public class DoubleCheckedLocking {
private volatile static Instance instance; //要延迟初始化的实例
public static Instance getInstance(){
if (instance == null){ //1.第一次检查
synchronized (DoubleCheckedLocking.class){ //2.加锁
if(instance == null){ //3.第二次检查
instance = new Instance(); //4.初始化
}
}
}
return instance;
}
}
注意:
1.为什么在第一次检查后加锁,而不是直接在getInstance()方法上加锁:
这是因为在多线程的环境下,多个线程竞争访问getInstance()方法,若在该方法上加锁,则多个线程会频繁的获取释放该锁,效率低下,而在第一次检查后加锁,当instance没被初始化时,即instance为null时,某个线程通过第一次检查,然后获取了锁,再通过第二次检查后对instance进行了初始化,之后,instance就不再为null了,之后所有线程便通不过第一次检查,也就不会再获取、释放锁了,提高了程序执行效率。
2.为什么instance声明的时候要加volatile关键字:
上述程序中第4步初始化操作在系统底层不是一步完成的,分成以下三步:
当给instance加上了volatile关键字后,可以禁止上述的指令重排序操作,也就不会产生上述多线程情况下的错误了。