经典j2ee设计模式Double-Checked Locking失效问题

来源:互联网 发布:json数组没有key 编辑:程序博客网 时间:2024/05/20 16:13

双重检查锁定失效问题,一直是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(); 
             }
 
          }
 
        
} 
        
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 classLazyFoo { 
    
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

原创粉丝点击