关于延迟初始化

最近常在代码中见到延迟初始化,想和大家聊聊这个小话题。最简单的延迟初始化很容易想,提起键盘我就能敲出来啊:

public class DelayInitializationWithSingleThread {
    private static Object foo;
    
    public static Object getInstance() {
        if(foo == null)
            this.foo = new Object();
        return this.foo;
    }
}

这个延迟初始化,单线程下很完美,多线程下岂不是要乱套!那简单,那我在方法上加一个同步操作好了:

public class DelayInitializationWithMultiThread {
    private static Object foo;
    
    public synchronized static Object getInstance() {
        if(foo == null)
            this.foo = new Object();
        return this.foo;
    }
}

但是加锁需要上下文切换,是个很耗系统资源的操作,每次调用getInstance()都要执行一次上下文切换,有些没必要。我只需要在第一次初始化的时候进行加锁就好了啊,这就是著名的DCL(Double Check Lock):

public class DelayInitializationWithDCL {
    private static Object foo;
    
    public synchronized static Object getInstance() {
        if(foo == null) {
            synchronized (Test.class) {
                if(foo == null)
                    this.foo = new Object();
            }
        } 
        return this.foo;
    }
}

看起来很不错。
等一下,还记得JVM的可见性吗?每个线程的变量都是在各自线程的缓存中,各自的工作变量相互不可见,必须等到JVM将其刷到公用的主存里才能对其他线程可见。也就是说A线程初始化foo后,B线程不一定能看得到,在不知道的情况下,那B会再执行一遍初始化,C线程也不一定看得见,他继续。。。这还只是个简单的示例代码,要是工作代码执行一些复杂的初始化的话,那不是可能造成对象失效或者脏数据?好吧我不敢再往下想了,赶紧给变量foo加上消除线程缓存作用的volatile关键字。

public class DelayInitializationWithVolatiledDCL {
    private volatile static Object foo;
    
    public synchronized static Object getInstance() {
        if(foo == null) {
            synchronized (Test.class) {
                if(foo == null)
                    this.foo = new Object();
            }
        }
        return this.foo;
    }
}

呼呼。。这下终于完成了,看着很完美。但是啊,有些复杂,万一哪天我头脑发热,忘记加volatile咋办呢。好吧,为了有更好的方案,继续改造:

public class DelayInitializationWithInitalizationPlaceHolder {
    private static class ResourceHolder {
        public static Object foo = new Object(); 
    }
    public synchronized Object getInstance() {
        return ResourceHolder.foo;
    }
}

当getInstance()第一次被调用时,JVM会加载ResourceHolder类,并将其静态变量初始化,以后再次调用就能直接获取到初始化的foo了,不需要加锁,不需要volatile,简直太棒了,完美!

你可能感兴趣的:(关于延迟初始化)