单例设计模式被打破? [复制]

Posted

技术标签:

【中文标题】单例设计模式被打破? [复制]【英文标题】:Singleton design pattern broken? [duplicate] 【发布时间】:2012-01-11 00:30:04 【问题描述】:

可能重复:Efficient way to implement singleton pattern in Java

我原以为下面的类是线程安全的单例,但阅读 http://taskinoor.wordpress.com/2011/04/18/singleton_multithreaded/ 似乎不是。

public class ThreadSafeSingleton 

    private static ThreadSafeSingleton ref;

    private ThreadSafeSingleton()

    

    public ThreadSafeSingleton getSingletonObject()

        if(ref == null)
            ref = new ThreadSafeSingleton();
           
        return ref;

    

根据文章,唯一真正线程安全的单例是 -

public class ThreadSafeSingleton 

    private static ThreadSafeSingleton ref = new ThreadSafeSingleton();

    private ThreadSafeSingleton()

    

    public ThreadSafeSingleton getSingletonObject()
        return ref;

    

这样对吗?

【问题讨论】:

请检查右侧栏中的“相关”列表(顺便说一下,这与您输入问题标题后出现的列表完全相同)。当然这个问题之前已经被问过无数次了:) 绝对不是唯一的线程安全方式,甚至不是最好的线程安全方式。确实,顶部不是线程安全的,而底部似乎是。 我的理解是,底层解决方案是“最好的”线程安全的单例实现(尽管最好的可能取决于特定的用例)。 Glowcoder,你推荐哪种方式更好? @increment 你如何定义“最佳”?不同的解决方案有不同的权衡。如果您以后只需要该对象,这当然不是昂贵的初始化的最佳选择。 @user470184,这不是实现线程安全单例的唯一方法。博客里没有说。有许多特定于平台的解决方案,在博客文章的末尾简要描述了这些解决方案。特别是,如果您使用 J2SE 5.0 或更高版本,则可以将volatile 与 DCLP 一起使用,因此也可以进行延迟初始化。 【参考方案1】:

这不是唯一的线程安全单例,但这是正确的。另一种方法是同步第一个示例中创建单例实例的代码。但是这样做的问题是您必须同步代码,这可能是也可能不是问题。另一个可能的问题是单例没有延迟初始化。同样,这可能是也可能不是问题,具体取决于架构和要求。还有另一种奇怪的模式可以解决这个问题:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom。

【讨论】:

AKA,Singleton 模式的 Bill Pugh 解决方案:en.wikipedia.org/wiki/…【参考方案2】:

是的,这篇文章是正确的。

在上面的示例中,如果该方法同时被两个函数调用,它们可能都将 ref 视为 null,因为在另一个函数检查之前,它们都没有真正完成创建和分配。

在下面的示例中,ref 在类加载时分配一次,然后任何东西都可以访问它。

【讨论】:

【参考方案3】:

是的,没错。

在第一种情况下,如果两个线程同时调用getSingletonObject(),则存在以两个单例实例结束的风险。

在第二种情况下,该方法只返回对在类加载期间创建的现有对象的引用,这是由 JVM 以线程安全的方式完成的。

第二个在线程安全方面比第一个好很多。

【讨论】:

【参考方案4】:
if(ref == null)

    ref = new ThreadSafeSingleton();
            

return ref; 

同时运行的两个线程可能会命中if (ref == null)。两个线程可以看到ref 实际上是空的。然后两个线程将创建一个新实例。然后可以给定两个线程单独的实例。现在您有两个不同的“单例”对象实例。

在这个 sn-p 中没有适当的机制来防止上述竞争条件。

【讨论】:

【参考方案5】:

有几种不同的方法可以实现线程安全的单例对象。静态初始化(底部方法)是其中一种方法。

您也可以使用static inner class、dependency injection、同步getInstance(),甚至是double-checked locking pattern in Java 5+,只要将持有者变量声明为volatile。请参阅Java Concurrency in Practice 了解更多信息。

其中,如果我必须构建一个实际的单例,我更喜欢第一种方法。否则我喜欢在Guice 中使用范围。

【讨论】:

以上是关于单例设计模式被打破? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

设计模式单例模式

单例模式有啥缺点吗? [复制]

为啥不鼓励使用单例模式? [复制]

如何在 C# 中实现单例设计模式? [复制]

现在更好的 Java 单例模式? [复制]

正确的单例设计? [复制]