通常DCL都是这么写的
public class LazySingleton {  
    private static LazySingleton m_instance = null;  
  
    private LazySingleton() {  
    }  
  
    public static LazySingleton getInstance() {  
         if (m_instance == null) {  
             synchronized (LazySingleton.class) {  
                  if (m_instance == null) {  
                      m_instance = new LazySingleton();   /*网上的分析DCL失效原因时,认为是这里构造函数执行之前,使实例成为非 null而被其他线程所夺取cpu。并读取到没有执行构造函数实例应用*/                  }  
              }  
         }  
         return m_instance;  
     }  
}在java并发编程实践里面对原子性的描述有2层含义:1.一组语句作为单独的不可分割的单元运行2.一个由同一个锁保护的synchronized块一次只能由一个线程执行
似乎DCL失效时,原子性的第一个含义没做到?是我对synchronized块的原子性认识有错么?

解决方案 »

  1.   

    DCL失效原因是:获得锁的线程正在执行构造函数的时候,其他的线程执行到第一次检查if (m_instance == null)的时候,会返回false,因为已经在执行构造函数了,就不是null,因此,会把没有构造完全的对象返回给线程使用。这是不安全的。
      

  2.   

    3楼的,我是想问这里面的构造函数不是已经包含在了synchronized{}块中了么,为什么不会构造完全(破坏了原子性)?

    synchronized(this)
    {
      i++;
    }
    i++本来不是原子操作,但套在synchronized块里时就是原子操作了啊
      

  3.   

    我是很怀疑synchronized对涉及到内存分配的构造函数有问题
      

  4.   


    是正在构造的过程中,比如说:构造这个对象需要1个小时的时间,在开始构造以后,半个小时的时候,另一个线程来访问,此时的(m_instance == null)不成立,然后这个线程就返回了这个对象的引用。对象是在堆上面进行构造的,引用只是一个指向对象的指针而已,当然可以返回正在构造的对象的引用了。
      

  5.   

    楼上这么解释的话,我明白一半了,在多核和多cpu里面是这样DCL失效的,那么单核的情况下DCL失效是怎么发生的呢?
      

  6.   

    在单核的cpu上如果线程A执行m_instance = new LazySingleton();那么由于synchronized的原子性,在 LazySingleton函数完成前是不能调度线程B运行的吧?
      

  7.   


    问题就在于m_instance = new LazySingleton();它不是原子的这跟jvm的内存模型有关系,你可以查一下相关的资料
      

  8.   


    连double d = 1L;这样的赋值语句都不是原子的,何况是对象的构造!
      

  9.   


    不好意思写错了double d = 1.0;或者long l = 1L;都不是原子的
      

  10.   

    new操作符会阻塞线程,在对象初始化完成之前,是不会将引用返回的。
      

  11.   

    我知道double d = 1.0;或者long l = 1L不是原子的,但是如果这么写就该是原子的了吧?
    Object lock=new Object();
    synchronized(lock)
    {
      double d = 1.0;
    }
    同理
     synchronized (LazySingleton.class) { 
       m_instance = new LazySingleton();
    }
    也该是原子的吧?
      

  12.   

    Object lock=new Object();
    synchronized(lock)
    {
      double d = 1.0;
    }
    如果你将这段写在方法里,那没什么意义。
    每个方法都有自己独立的运行时栈,所以每个线程都有一个lock,那么每个线程都能成功的获得lock的实例锁。
    所以赋值的操作,当然不是原子的。
      

  13.   

    讨论偏题了啊,看我主楼的问题,我意思是如果是单核的cpu,DCL失效时,synchroized块的原子性也失效了,是我对synchroized块的原子性理解错了么?
      

  14.   

    http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1class SomeClass {
      private Resource resource = null;
      public Resource getResource() {
        if (resource == null) {
          synchronized {
            if (resource == null) 
              resource = new Resource();
          }
        }
        return resource;
      }
    }
    DCL relies on an unsynchronized use of the resource field. That appears to be harmless, but it is not. To see why, imagine that thread A is inside the synchronized block, executing the statement resource = new Resource(); while thread B is just entering getResource(). Consider the effect on memory of this initialization. Memory for the new Resource object will be allocated; the constructor for Resource will be called, initializing the member fields of the new object; and the field resource of SomeClass will be assigned a reference to the newly created object. However, since thread B is not executing inside a synchronized block, it may see these memory operations in a different order than the one thread A executes. It could be the case that B sees these events in the following order (and the compiler is also free to reorder the instructions like this): allocate memory, assign reference to resource, call constructor. Suppose thread B comes along after the memory has been allocated and the resource field is set, but before the constructor is called. It sees that resource is not null, skips the synchronized block, and returns a reference to a partially constructed Resource! Needless to say, the result is neither expected nor desired. 
      

  15.   

    http://www.ibm.com/developerworks/cn/java/j-dcl.html
    我知道DCL失败的原因,上面那篇文章讲的更细点,我是想讨论下synchronized块的原子性的问题:一组语句作为单独的不可分割的单元运行。要么都做,要么都不做。根据这个原子性,似乎在单核的cpu(当然也没有超线程)上DCL是不可能失效的,因为原子性线程A在构造函数完成前是不能让出cpu的,而在多核的cpu上,线程b可以在另一个内核上执行,这有可能使得DCl失效。这么分析下来,似乎又违反了java跨平台性,晕了
      

  16.   

    http://www.blogjava.net/weidagang2046/articles/3494.html
    搞定了,DCL失效原因之2
      

  17.   

    JMM 的关系,由于共享变量在本地线程栈中有个复制缓存,可能会因为不能及时更新的关系,而使用得双重检查同步锁同样会产生多个对象的实例。JDK 1.5 之前无任何解决方案。JDK 1.5 上,在判断是否为 null 的变量上加上 violatile 关键字就可以了。
      

  18.   

    DCL 对于现代计算机来说是一种闲得蛋疼的做法。不要认为 new 一个对象是一个很耗时的工作的,Java 已经将这一块优化到了极致,只有几条 CPU 指令而已,其堆内存的分配速度也是非常快的,有文献称 Java 中堆内存的分配速度比 C 语言还快。
      

  19.   

    呵呵,谢谢楼上各位回答,受教了。java线程同步块的原子性是锁实现的原子性不同于数据库事务的原子性。我生搬硬套了
    public class LazySingleton {   
      private static LazySingleton m_instance = null;   
       
      private LazySingleton() {   
      }   
       
      public static LazySingleton getInstance() {   
      if (m_instance == null) {   
      synchronized (LazySingleton.class) {   
      if (m_instance == null) {   
      m_instance = new LazySingleton(); /*执行到这里是可以发生线程调度的,让其他线程执行,获取cpu*/  }   
      }   
      }   
      return m_instance;   
      }   
    }