繁体   English   中英

关于单身设计模式

[英]Regarding singleton design pattern

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

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;
  }
}

但请告诉我第二个设计如何优于以前的设计.. !!

Josh Bloch建议使用枚举:

   public enum Foo {
       INSTANCE;
   }

有关解释,请参阅Google I / O 2008上的Effective Java Reloaded talk。

综上所述:

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

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

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

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

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

渴望实例化与懒惰初始化

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

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

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

线程安全和性能

第二种设计的另一个优点是性能更高。 在多线程环境中,您的第一个设计将要求每个线程在获取实例之前获取锁,即使在已经实例化之后也是如此。 您可以通过使用双重检查锁定Bill Pugh方法来解决这个问题

Enum Way

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

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

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

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

几点说明:

  1. 您的第一个示例在多线程方案中无法正常工作。 您的ref应该是volatile变量,需要使用锁定进行双重检查。

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

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

为什么在此双重检查锁定示例中使用volatile

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM