关于单身设计模式

时间:2012-04-20 17:28:43

标签: java design-patterns singleton

我正在探索单身设计模式,我开发了一个类......

public class SingletonObject {
  private static SingletonObject ref;       
  private SingletonObject () { //private constructor
  }     
  public static synchronized SingletonObject getSingletonObject() {
    if (ref == null)
      ref = new SingletonObject();
    return ref;
  } 

  public Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException ();
  }
}

但同步是​​非常昂贵的,所以我转向热切创建的实例的新设计,而不是懒惰创建的实例..

public class Singleton {
  private static Singleton uniqueInstance = new Singleton();
  private Singleton() {
  }
  public static Singleton getInstance() {
    return uniqueInstance;
  }
}

但请告诉我第二种设计比以前的设计更有优势.. !!

6 个答案:

答案 0 :(得分:5)

Josh Bloch建议使用枚举:

   public enum Foo {
       INSTANCE;
   }

有关解释,请参阅他在Google I / O 2008上的Effective Java Reloaded演讲。

总结:

  

“这种方法在功能上等同于公共领域   方法,除了它更简洁,提供序列化   机械免费,并提供一个铁定的保证   多个实例化,即使面对复杂   序列化或反射攻击。虽然这种方法尚未实现   广泛采用,单元素枚举类型是最好的方法   实现单身人士。“

答案 1 :(得分:2)

如您所述,第二种解决方案可避免同步成本。它也更简单,更清洁,更易于阅读和维护。但是它有一个小问题:你错过了final的{​​{1}}限定符,这意味着它可能无法保证在并发环境中是线程安全的(尽管在这个具体情况下我不认为这会在现实生活中引起任何明显的问题......但最好是关于线程安全的安全方面)。幸运的是,这很容易解决。

它的另一个缺点是,只要引用了类private static Singleton uniqueInstance,就会创建单例对象,即使它实际上从未被实际使用过。如果创建成本很高,这可能是一个问题。使用Initialization-on-demand Holder idiom可以避免这种情况。

答案 2 :(得分:1)

你的第二个设计更好,因为它更简洁,更容易阅读。此外,正如您所提到的,它避免了每次使用单例时同步的成本。

第二种设计的一个缺点是,即使你从未使用它,也会产生实例化单例的内存和cpu成本。

答案 3 :(得分:1)

渴望实例化与延迟初始化

您的第二个设计使用急切实例化而不是延迟初始化。这不一定更好或更糟,取决于哪种适合您的应用。

通常情况下,最好在以下情况下使用延迟初始化:

  • 如果您的应用程序可能不需要创建您的类的实例
  • 如果实例化您的课程费用昂贵,而且您宁愿将操作推迟到尽可能晚的时间

线程安全和性能

您的第二个设计的另一个优点是它的性能更高。在多线程环境中,您的第一个设计将要求每个线程在获取实例之前获取锁,即使在已经实例化之后也是如此。您可以使用double-checked lockingBill Pugh approach来解决此问题。

Enum Way

与您的两种设计不同的方法是Enum way,它使用具有单个值的枚举。由于Enums可以有方法和成员变量,因此您可以模仿普通类所具有的行为类型。这是一个很好的,铁定的方式来创建一个单身人士,并由Joshua Bloch推荐。

答案 4 :(得分:0)

您还应该使变量指向您的单身final

public class Singleton {
    private static final Singleton uniqueInstance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

此实现使ClassLoader实例化单例的实例,从而提供线程安全性。另一种模式使用enum,但我个人认为该实现是一种代码味道。

答案 5 :(得分:0)

很少注意到:

  1. 您的第一个示例在多线程方案中无法正常工作。您的引用应为volatile变量,并且需要进行双重检查。

  2. 您的第二个示例没有额外的synchronization费用。但是你可以有效地实现Lazy Singleton而不是热切的Singleton。

  3. 有关详细信息,请参阅下面的SE问题:

    Why is volatile used in this example of double checked locking