为什么这种模式被认为是破碎的?它看起来很好吗?有什么想法吗?
public static Singleton getInst() {
if (instace == null) createInst();
return instace;
}
private static synchronized createInst() {
if (instace == null) {
instace = new Singleton();
}
}
答案 0 :(得分:21)
您可以通过使用“volatile”关键字正确处理单例实例来避免这种情况
答案 1 :(得分:11)
整个讨论是一个巨大的,无休止的大脑时间浪费。 99.9%的时间,单身人士没有任何重大的设置成本,并且没有任何理由设计设置来实现非同步保证延迟加载。
这就是你在Java中编写Singleton的方法:
public class Singleton{
private Singleton instance = new Singleton();
private Singleton(){ ... }
public Singleton getInstance(){ return instance; }
}
更好的是,让它成为一个枚举:
public enum Singleton{
INSTANCE;
private Singleton(){ ... }
}
答案 2 :(得分:7)
我不知道它是否被破坏,但由于同步这种方法相当昂贵,它并不是真正最有效的解决方案。 更好的方法是使用'Initialization On Demand Holder Idiom',它在第一次需要时将你的单例加载到内存中,顾名思义,因此延迟加载。 这个习惯用法带来的最大好处是你不需要同步,因为JLS确保类加载是串行的。
关于该主题的详细维基百科条目:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom
要记住的另一件事是,由于Spring和Guice之类的依赖注入框架已经出现,正在创建类实例并由这些容器创建和管理,如果需要它们将为您提供Singleton,因此它不值得除非你想学习模式背后的想法,这是有用的。 另请注意,这些IOC容器提供的单例是每个容器实例的单例,但通常每个应用程序都有一个IOC容器,因此它不会成为问题。
答案 3 :(得分:6)
问题如下:您的JVM可能会重新排序您的代码,并且字段对于不同的线程并不总是相同。看看这个:http://www.ibm.com/developerworks/java/library/j-dcl.html。使用volatile关键字应该解决这个问题,但是在java 1.5之前它已经破坏了。
大多数时候,单次检查锁定的速度已经足够快,请尝试以下方法:
// single checked locking: working implementation, but slower because it syncs all the time
public static synchronized Singleton getInst() {
if (instance == null)
instance = new Singleton();
return instance;
}
另请参阅有效的java,在这里您可以找到关于此主题的精彩章节。
总结一下:不要做双重检查锁定,有更好的idoms。
答案 4 :(得分:4)
Initialization On Demand Holder Idiom,是的,就是这样:
public final class SingletonBean{
public static SingletonBean getInstance(){
return InstanceHolder.INSTANCE;
}
private SingletonBean(){}
private static final class InstanceHolder{
public static final SingletonBean INSTANCE = new SingletonBean();
}
}
虽然Joshua Bloch也在Effective Java第2章第3项中推荐了Enum单身人士模式:
// Enum singleton - the prefered approach
public enum Elvis{
INSTANCE;
public void leaveTheBuilding(){ ... }
}
答案 5 :(得分:2)
这不回答你的问题(其他人已经做过),但我想告诉你我对单身/懒惰初始化对象的体验:
我们的代码中有几个单身人士。一旦我们必须将构造函数参数添加到一个单例并且遇到严重问题,因为在getter上调用了这个单例的构造函数。只有以下可能的解决方案:
最后,最后一个选择是要走的路。现在我们在应用程序启动时初始化所有对象并传递所需的实例(可能是一个小接口)。我们没有后悔这个决定,因为
答案 6 :(得分:2)
这里的大部分答案都是正确的,为什么它被打破,但是不正确或暗示可疑的解决方案策略。
如果你真的那么,真的必须使用单例(在大多数情况下你不应该,因为它破坏了可测试性,结合了如何构建类的逻辑class的行为,使用单例知道如何获取单一的类,并导致更脆弱的代码)并且关注同步,正确的解决方案是使用a static initializer来实例化实例。
private static Singleton instance = createInst();
public static Singleton getInst() {
return instance ;
}
private static synchronized createInst() {
return new Singleton();
}
Java语言规范保证静态初始化器只运行一次,第一次加载类时,并以保证的线程安全方式运行。