懒汉式的延迟加载可以解决线程安全和节省内存,但又会引起性能不足。
按照上一篇文章的代码,a,b多个线程执行情况下,如果a先进入了方法处于允许状态,那b线程则只能处于监听状态。
通过调式效果如下
之前是让其他线程在方法外面待着,那是否可以让它们也先进方法里面来等待呢?这就跟餐厅排队等一样,站在外面风吹日晒,不如先让顾客进大厅进行排队。
代码改版后的2.1
public class LazySingletion {
private LazySingletion(){
}
private static LazySingletion single;
protected static LazySingletion getInstance(){
synchronized(LazySingletion.class){
if(single == null ){
single = new LazySingletion();
}
}
return single;
}
}
这里有人会产生疑问,这换汤不换药啊,还是需要等待。所以这段代码还有改进的余地。
改进思路:1.可以先判断是否已经存才对象,再来考虑是否排队
2.2版本
public class LazySingletion {
private LazySingletion(){
}
private static volatile LazySingletion single;
protected static LazySingletion getInstance(){
if(single == null){
synchronized(LazySingletion.class){
if(single == null ){
single = new LazySingletion();
}
}
}
return single;
}
}
为了避免执行重排序问题,这里可以加了一个关键字 volatile。
LazySingletion single = new LazySingletion();这行代码在源码上是一行,但是它在执行的过程却是多个阶段
1.分配内存空间,比如班上来了一位新同学,先提前安排一个地方。
2.初始化对象,给新同学配上桌子板凳,位置上并没有人。
3.对象指向内存地址。新同学真正来到学校并入座分配的位置,该位置真正有人坐。
这3个阶段不存在数据间的相互依赖,所以可以进行指令重排序。多线程情况下,重排顺序为1-3-2时,当判断if(single == null)时,执行到了第3步骤,地址不为空,因为该地址已经分配出去(座位虽然是空,但已经有属于的同学了) ,最后返回对象。由于并没有初始化完成,在使用该地址指引的对象时就会抛出异常(新同学还没有真正的来到教室入座),