前java5:单例模式:双重检查锁定与静态工厂方法

时间:2013-11-19 17:25:15

标签: java oop design-patterns

预JSE5:

以下两种在Pre-JSE5中实现Singleton设计模式的方法有何区别?在我看来,两者都只是延迟加载。

即使是静态工厂方法也不是延迟加载,还有什么区别?

双重检查锁定方法:

public class Singleton {
  private static volatile Singleton sic; 
  private Singleton() {}

  public static Singleton getInstance() {
    if (sic == null) {
      // Let's make it thread safe using 'synchronized'
      synchronized(Singleton.class) {
        if (sic == null) {
          sic = new Singleton();        
        }
      }
    }
    return sic;
  }
}

静态工厂方法

public class Singleton_StaticFactory {
  private static final Singleton_StaticFactory s = new Singleton_StaticFactory();
  private Singleton_StaticFactory() {}
  public static Singleton_StaticFactory getInstance() {
    return s;
  }
}

更新

Russell Zahniser在他的回答中解释了为什么静态工厂实现以一种不想要的方式提前加载单例,当一些其他静态方法(如果存在)在'getInstance'方法之前访问时。所以,与我上面提到的不同,两者在“延迟初始化”方面都不一样。

我仍然想知道是否还有其他差异。

3 个答案:

答案 0 :(得分:3)

在Java 1.5之前,在写入volatile字段和从同一字段读取之间没有发生 - 之前的边缘。所以,假设线程A调用getInstance(),看到sic == null并实例化新的Singleton并将其分配给sic。稍后,线程B调用getInstance(),看到sic != null并返回,而不通过synchronized。在这种情况下,在创建sic的线程A和读取它的线程B之间的边缘之前发生 no 。这意味着,除非Singleton即使在面对不安全的发布(大多数对象不是)时也是线程安全的,否则存在竞争条件且代码不是线程安全的。

此外,在没有安全发布的情况下使对象成为线程安全的方法之一是标记其所有字段final。这可以确保读取其中一个final字段的任何对象将看到它在构造函数末尾的状态(只要您不在构造函数之外泄漏this)。但是这种保证本身只是在Java 1.5中添加的。这意味着在Java 1.5之前,在没有安全发布的情况下确实没有任何方法可以使对象安全,因此双重检查锁将始终是不安全的。

所有这些仅适用于你的单身人士有州的情况 - 当然 - 否则,没有国家需要安全地发布线程!

答案 1 :(得分:2)

简短的回答是,在Java 1.4中,双重检查锁定无法可靠地工作。

请参阅http://en.wikipedia.org/wiki/Double-checked_locking

或Java Concurrency in Practice,第16.2节

@yshavit说的答案很长。 : - )

答案 2 :(得分:1)

如果Singleton_StaticFactory有任何其他静态方法(或静态字段或公共构造函数),则调用它们将触发加载该类并初始化单例。所以在这种情况下,两者并不完全相同;否则他们就是。