其实不用太纠结什么“对象分配了引用,但还没创建完毕,另外一个线程发现引用不为null,使用报错“。  就按代码逻辑去理解, DCL配合volatile是完全正确的单例模式!

解决方案 »

  1.   

    我没有读完全文,也不知道你为什么说“没有运行完会释放锁”,public static Singleton getInstance()
    { if (instance == null)
      {      synchronized(Singleton.class) {  //1
          if (instance == null)          //2
            instance = new Singleton();  //3
        }
      }
      return instance;}先检查instance是否为null,如果为null则创建新实例。这时锁定Singleton.class。锁定进去了之后,再检查一下instance是否为null,就怕刚才有人已经把instance创建好了。锁定前后各检查一次,就叫双重检查。
      

  2.   

    或者这么说,设有两个线程A、B。线程A将要运行第7行,由于CPU调度原因被阻塞。线程B现在从第1行开始运行,一直运行到第14行,instance被创建。然后线程A继续,运行第7行,成功获得锁。请问它此时要不要创建instance的实例?
      

  3.   

    public static Singleton getInstance()
    { if (instance == null)
      {      synchronized(Singleton.class) {  //1
          if (instance == null)          //2
            instance = new Singleton();  //3
        }
      }
      return instance;
    个人认为问题不是出现在释放锁 而是博主说的代码优化
    就是在创建对象的时候可能会存在1分配内存2创建引用关系3初始化对象 这样的优化创建方式
    这样在//3处可能尚未完全创建对象时即2创建引用关系时由于CPU调度当前线程失去CPU
    这时在最外层的 if (instance == null)处(非//2处)条件判断失败其他线程会直接返回instance 而此时线程1并未失去锁
    另外,据说jdk1.5以后此问题已经被优化了但是。。不太肯定
      

  4.   


    java的内存模型中,构造函数的执行和instance的内存分配是无顺序的,不是说一定在构造函数执行完,instance才不等于null。
    所以存在一种情况,构造函数执行过程中,instance已经不为null,而刚好此时另一个getInstance()调用发生,直接返回了一个初始化不完整的instance
      

  5.   

    JMM 的问题,在 JDK 1.5 以及后使用 volatile 关键字的话就不存在这个问题了。至于 JDK 1.5 前 DCL 为什么会有问题,详细的还是需要看看专家们是怎么说的,参考以下文章:
    The "Double-Checked Locking is Broken" Declaration 
    http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
      

  6.   

    volatile出现后,基本就不用考虑双重锁定的问题了;
    如同单例模式中的一样;
      

  7.   

    不是吧,volatile不是会大大影响性能的吗? 双重锁定,最起码是使用的改变量的时候才锁啊,volatile是只要有变化,各线程全部更新一次。
      

  8.   

    用了双重锁定出问题,不用呢,如下代码,不是更加会出问题么public static Singleton getInstance()
    {
          synchronized(Singleton.class) {  //1
          if (instance == null)          //2
            instance = new Singleton();  //3
      }
      return instance;
      

  9.   

    双检锁的问题在于对象创建中的进程抢占,不是任何场景都需要双检锁+volatile的。
    而且java在1.2之后就已经有了更好的饿单例实现:利用内部类创建实例,简单高效,没有任何线程问题。
      

  10.   

    不用双重检查锁的单例没有线程安全问题   却有效率问题。  效率在于每次来的都要得到锁。
    用双重检查锁的单例有线程安全问题    效率好一些。 线程安全问题在于 讲一个没有完全实例化的对象释放出去。public static Singleton getInstance()
    {
     
     if (instance == null)//4
      {
     
          synchronized(Singleton.class) {  //1
          if (instance == null)          //2
            instance = new Singleton();  //3
        }
      }
      return instance;这段代码 中,一个线程看到第四步  发现 不为null,拿去使用了。但是实际是这个不为null的对象,并没有完全实例化。
    原因在于:   3处这个地方代码只有一行,真正意义上完成一个对象需要三步。
    1,分配对象的内存空间      1买了房子
    2,为该对象的属性赋值     2给房子装修
    3,初始化该对象。            3 把名字给你。
    这个顺序不一定,加入说  3 执行了  再执行2  就出问题了.解决办法是  给instance的修饰加上volatile 。这上这个字后  保证不会重排序。这样得到的就是正确的对象。回答完毕。
      

  11.   

    双重检查锁定只适合 那个变量从 null  变成 not null  的情况(也就是单例)。其它情况下没有什么实际意义。
    第一次检查没有锁定的成本。如果它频繁地在 null 和 not null 之间切换的话,这第一个检查还不如直接省掉算了。