Gangmax Blog

[转贴]关于DCL(double check lock)问题的解释

Migarated from here at ‘2012-05-22 17:35:03’.

参考了这里

 
DCL是lazy initialization的一种技巧,理论上应该是线程安全的,但是在实际应用中却正相反。

详情请见这里

前提1:Java 使得它的每个线程使用独立的处理器和一个私有的内存空间,每一个线程的私有内存都会与主存交互与同步。synchronized 块会建立一个内存壁垒(memory barrier) ,当进入synchronized块时会建立一个read barrier :它会使得Thread的本地内存无效,并且从主存中读取所有的变量的值。当退出一个synchronized块时,它会确保将对所有本地内存的改变都同步到主存中。并且synchronized也会保证这段代码是原子操作。

前提2:假设一个new操作需要以下两个步骤(也许更多)1.分配相应内存2.调用构造函数。

前提3:我们不能在书写我们的代码时预计指令真正的执行顺序,他可能由于编译器,处理器和缓存机制的干预而变化。规范规定:编译器编译器,处理器和缓存机制可以任意改变指令顺序,只要不影响结果的值。实例:

1
2
3
4
5
6
7
8
9
public Resource getResource(){
   if (resource == null){ // a:    
       synchronized(this){
          if (resource==null){
              resource = new Resource();  //b:
          } 
       }
    }return resource;//c
}

解释:

有了以上的前提,我们可以讨论DCL是如何导致线程不安全的。假设:一个线程t1进入getResource()方法,并且执行到b处。这时另一个线程t2进入。当它在a处时t1刚好进行完分配内存的工作,而没有调用构造函数。这时T2会认为resource不为null因而直接来到c处,得到了一个错误的resource实例。显而易见会导致错误。相关参考java language specification chapter 17

Comments