为什么静态单例是避免“DCL”的简单而优雅的解决方案?

时间:2013-05-15 15:29:54

标签: java singleton dcl

当需要单身时,静态字段是一个优雅的解决方案吗?

class HelperSingleton {
  static Helper singleton = new Helper();

  public static Helper getInstance() {
       return singleton;
     }
  }

当两个线程同时访问getInstance时,字段singleton是否有可能未完全初始化?或者查看辅助对象的字段的默认值,而不是构造函数中设置的值? 静态单例也是懒惰初始化?

我是说, static Helper singleton = new Helper(); 这个分配是原子的吗?并且不会返回默认值?

4 个答案:

答案 0 :(得分:1)

我认为单身人士最优雅的解决方案是:

public enum Singleton {

    INSTANCE;

    public Singleton getInstance() {
        return INSTANCE;
    }
}

使用单例模式有些问题,因为有时您不知道真正只有其中一个。例如,当您使用类加载器时,请考虑这种情况。

This问题(以及其他问题)顺便说一下。

答案 1 :(得分:1)

1)当线程访问静态getInstance时,第一次类加载器必须加载HelperSingleton类,它将在加载类之前锁定其他线程。因此存在隐式同步。 J.Bloch“Effective Java”Item 71 现代VM将仅同步字段访问以初始化类。初始化类后,VM将修补代码,以便后续访问该字段不涉及任何测试或同步。

2)你的单身人员很懒,因为只有一个接入点 - getInstance。不仅按需创建实例,而且按需加载整个类。一个类不会被初始化 直到它被使用[JLS,12.4.1]。

答案 2 :(得分:0)

查看http://en.wikipedia.org/wiki/Singleton_pattern

有很多样式可以解释好/坏。

答案 3 :(得分:0)

如果是这样的话:

class HelperSingleton {
  static Helper singleton = null;
  public static Helper getInstance() {
      if(singleton == null) {
         singleton = new Helper();
      }
      return singleton;
    }
}

这里可能存在这样的可能性:

  1. 线程1调用getInstance()方法并确定singletonnull

  2. 线程1进入if块,但在创建新实例之前被线程2抢占。

  3. 线程2调用getInstance()方法并确定该实例为null

  4. 线程2进入if块并创建一个新的Helper对象,并将变量singleton分配给此新对象。

  5. 线程2返回Singleton对象引用。

  6. 线程2被线程1抢占。

  7. 线程1从它停止的地方开始并执行singleton = new Helper();,这导致创建另一个Singleton对象。

  8. 线程1返回此对象。

  9. 所以我们最终得到了两个实例。创建单身人士的最佳方式是使用枚举作为已回答here

    static Helper singleton = new Helper(); 
    

    此处加载类时会创建Helper的新实例,singleton保存对该实例的引用。您可以在JLS 12.4.2中获得详细的初始化过程。