单例设计模式被打破? [复制]
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 中使用范围。
【讨论】:
以上是关于单例设计模式被打破? [复制]的主要内容,如果未能解决你的问题,请参考以下文章