Java 单例模式(Bill Pugh和#x27;的解决方案)

Java 单例模式(Bill Pugh和#x27;的解决方案),java,design-patterns,singleton,Java,Design Patterns,Singleton,我正在读关于singleton模式的维基,我不确定我是否理解了这一点:部分理解是正确的 简单来说:为什么Bill Pugh的解决方案比上面的例子更好? 这是因为一个静态类在实际使用之前或者类似的情况下并没有被VM加载,所以我们在使用getInstance()方法之前不创建对象吗? 另外,该方法仅在初始化对象时才是线程安全的吗?我认为Pugh先生的版本受到高度重视,因为它只在调用getInstance()时执行单例的实例化,即在加载类(持有getInstance方法的类)时不执行。如果你的单例构造

我正在读关于singleton模式的维基,我不确定我是否理解了这一点:部分理解是正确的

简单来说:为什么Bill Pugh的解决方案比上面的例子更好?

这是因为一个静态类在实际使用之前或者类似的情况下并没有被VM加载,所以我们在使用getInstance()方法之前不创建对象吗?
另外,该方法仅在初始化对象时才是线程安全的吗?

我认为Pugh先生的版本受到高度重视,因为它只在调用
getInstance()
时执行单例的实例化,即在加载类(持有getInstance方法的类)时不执行。如果你的单例构造做了一些花费不菲的事情,那么这对你来说可能是一个优势。如果你像世界上大多数人一样,他们的单例只是为了避免静态方法(而且你还没有进入依赖注入框架),那么我不会为此而失眠

正如文章所述,Pugh先生的方法比静态实例变量更慢——但实际上,如果Singleton类被加载,那么无论如何都将调用getInstance方法。因此,作为一个计算机科学练习,它是有用的,但在现实世界中,它的好处是有争议的


p、 我不太喜欢布洛赫先生的例子,因为使用枚举就是说我的单例是一个枚举,这对我来说不太合适(特别是有人说,正确的话,他说永远不要实现接口只是为了得到常量)

JLS保证类只有在第一次使用时才被加载(使单例初始化延迟),并且类加载是线程安全的(使
getInstance()
方法也是线程安全的)

至于为什么线程安全


因为第一次调用getInstance(),JVM将持有holder类同时,JVM不会第二次加载holder类:它将等待第一个线程完成类加载,在holder类的加载和初始化结束时,两个线程都会看到holder类正确初始化,从而包含唯一的单实例

是不是因为静态类不是 在虚拟机实际运行之前由虚拟机加载 用过或类似的东西,所以我们 在我们转向之前不要创建对象 到getInstance()方法

另外,该方法仅在初始化对象时才是线程安全的吗

它确保只创建一个实例,并且客户端只接收对完全初始化的实例的引用

是不是因为静态类不是 在虚拟机实际运行之前由虚拟机加载 使用

不只是一个静态类,任何类。在引用类之前不会加载它们。请参阅JLS-

或者类似的事情,所以我们不 在我们转向之前创建对象 getInstance()方法

没错

同样,该方法仅是线程安全的吗 在初始化 反对


分发引用是线程安全的,因此此方法始终是线程安全的,而不仅仅是在创建时。解释的关键部分如下所示:

嵌套类没有被引用 之前(因此加载了 由类加载器执行)比 调用getInstance()的时刻。 因此,此解决方案是线程安全的 不需要特殊语言 构造(即易失性或 已同步)


Bill Pogh的解决方案提供了惰性。

但是如果我的类除了singleton之外没有其他方法,那么它就不会在getInstance()之前被实例化是否仍被调用?Pugh先生示例中的静态类只有在调用getInstance时才会被加载,因为这是它第一次被引用。持有getInstance方法的公共类可能只有getInstance方法-因此,正如我上面从计算机科学的角度所说,这很有趣,但实际上它与对“传统简单方式”的背离我之所以选择enum的思维方式,是因为该类的实例数量是静态已知的。这就像西服的类示例,其中正好有四个;这里我们有一个enum,其中正好有一个成员,
INSTANCE
。尽管我能理解这样的观点,即这是令人困惑的。(然而“奇怪”这个静态定义的单一实例实际上只是强调了单一模式的弱点。对于一个关于“双顿”的枚举,
enum DatabaseConnection{LIVE,TEST}
?)@Andrzej Doyle我认为你的观点是有道理的。我想我看到当枚举值中有某种意义时使用枚举,也就是说,它具有函数意义,并且你只希望在系统中使用该值的单个表示形式。我见过CustomerLogic或AccountLogic等类作为单例实现(只是为了避免使用静态方法),在这种情况下,我看不到枚举是逻辑上合适的,即使从技术上讲它适合作业。@实际上,持有getInstance()方法的公共类通常至少有一个实例方法(例如java.awt.Toolkit)——重要的一点是getInstance()首先调用。嗨,我实际上不明白它是如何线程安全的。你能解释一下吗?因为第一次调用getInstance()时,JVM将持有holder类。如果另一个线程调用getInstance()同时,JVM不会第二次加载holder类:它将等待第一个线程完成类加载,在holder类的加载和初始化结束时,两个线程都会看到holder类正确初始化,从而包含唯一的单实例。