Double-Checked Locking失效问题

双重检查锁定失效问题,一直是JMM无法避免的缺陷之一.了解DCL失效问题, 可以帮助我们深入JMM运行原理.
要展示DCL失效问题, 首先要理解一个重要概念- 延迟加载(lazy loading).

非单例的单线程延迟加载示例:
class Foo {
    private Resource res = null;
    public Resource getResource() {
        //普通的延迟加载
        if (res == null)
            res = new Resource();
        return res;
    }
}

非单例的
多线程延迟加载示例:
Class Foo {
    private Resource res = null;
    public synchronized Resource getResource() {
        //获取实例操作使用同步方式, 性能不高
        if (res == null)
            res = new Resource();
        return res;
    }
}

非单例的
DCL多线程延迟加载示例:
Class Foo {
    private Resource res = null;
    public Resource getResource() {
        if (res == null) {
            //只有在第一次初始化时,才使用同步方式.
            synchronized(this) {
                if(res == null) {
                    res = new Resource();
                }
            }
        } // end if null
        return res;
    } 
}

Double-Checked Locking看起来是非常完美的。但是很遗憾,根据Java的语言规范,上面的代码是不可靠的。

出现上述问题, 最重要的2个原因如下:
1, 编译器优化了程序指令, 以加快cpu处理速度.
2, 多核cpu动态调整指令顺序, 以加快并行运算能力.

问题出现的顺序:
1, 线程A, 发现对象未实例化, 准备开始实例化
2, 由于编译器优化了程序指令, 允许对象在构造函数未调用完前, 将
共享变量的引用指向
部分构造的对象, 虽然对象未完全实例化, 但已经不为null了.
3, 线程B, 发现部分构造的对象已不是null, 则直接返回了该对象.

不过, 一些著名的开源框架, 包括jive,lenya等也都在使用DCL模式, 且未见一些极端异常.
说明, DCL失效问题的出现率还是比较低的.
接下来就是性能与稳定之间的选择了?

DCL的替代
Initialize-On-Demand
:

public class Foo {
    // 私有静态内部类, 只有当有引用时, 该类才会被装载
    private static class LazyFoo {
       public static Foo foo = new Foo();
    }

    public static Foo getInstance() {
       return LazyFoo.foo;
    }
}


维基百科的DCL解释:
http://en.wikipedia.org/wiki/Double-checked_locking
DCL的完美解决方案:
http://www.theserverside.com/patterns/thread.tss?thread_id=39606

你可能感兴趣的:(多线程,thread,框架)