为什么该模式被认为是破坏的?我觉得还不错吧?有任何想法吗?

public static Singleton getInst() {
    if (instace == null) createInst();
    return instace;
}

private static synchronized createInst() {
     if (instace == null) {
         instace = new Singleton(); 
     }
}
有帮助吗?

解决方案

乍一看似乎还不错,但这种技术有许多微妙的问题,通常应该避免。例如,考虑以下事件序列:

  1. 线程A注意到该值未初始化,因此它获得了锁定并开始初始化值。
  2. 允许编译器生成的代码更新共享变量,以指向部分构造的对象,然后在执行初始化。
  3. 线程B注意到共享变量已被初始化(或因此出现),并返回其值。由于线程B认为该值已经初始化,因此不会获取锁。如果B在A完成的所有初始化之前使用对象,则该程序可能会崩溃。

您可以通过使用“易失性”关键字来正确处理单例实例来避免这种情况

其他提示

在整个讨论的大脑时一个巨大的,无休止的浪费。的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(){ ... }
}

我不知道,如果它被打破,但它是不是真的由于同步是相当昂贵的最有效的解决方案。 更好的方法是使用“初始化按需持有人成语”,它加载你的单身在记忆中第一次被要求,顾名思义,从而延迟加载。 你用这个成语得到最大的好处是,由于JLS保证类加载串行你不需要同步。

在受试者详细Wikipedia条目: http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

要记住的另一件事是,由于依赖注入框架如Spring和吉斯已经出现,正在创建创建类的实例,并通过这些容器管理,如果需要的话,他们会为您提供一个Singleton,所以它是不值得打破你的头了,除非你想学的模式,这是非常有用的落后思想。 还要注意这些IOC容器提供的是每个集装箱实例是单身单身,但通常你必须每个应用程序一个IOC容器,所以它不会成为一个问题。

的问题是:你的JVM可能重新排序的代码和领域并不总是不同的线程相同。看看这个: http://www.ibm.com/ developerWorks中国/ JAVA /库/ J-dcl.html 。使用挥发性关键字应该解决这个问题,但它的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

初始化点播持有人成语,烨就是这样:

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

}

虽然约书亚布洛赫还建议枚举单例模式中有效的Java 第2章,项目3:

// Enum singleton - the prefered approach
public enum Elvis{
    INSTANCE;
    public void leaveTheBuilding(){ ... }
}

这并不能回答你的问题(其他人已经这样做了),但我想告诉你我对单例/延迟初始化对象的经验:

我们的代码中有几个单例。有一次我们必须向一个单例添加一个构造函数参数,并且遇到了一个严重的问题,因为这个单例的构造函数是在 getter 上调用的。可能的解决方案只有以下几种:

  • 为初始化此单例所需的对象提供静态 getter(或另一个单例),
  • 传递对象以初始化单例作为 getter 的参数或
  • 通过传递实例来摆脱单例。

最后,最后一个选择是要走的路。现在我们在应用程序启动时初始化所有对象并传递所需的实例(可能作为一个小接口)。我们并不后悔这个决定,因为

  • 一段代码的依赖关系一目了然,
  • 通过提供所需对象的虚拟实现,我们可以更轻松地测试我们的代码。

这里的大多数答案都是为什么它被打破正确的,但不正确或暗示可疑策略的解决方案。

如果你真的,的真正的必须使用单(在大多数情况下,你的不该,因为它破坏的可测性,如何建设一类的联合收割机逻辑类的行为,这乱丢使用单与如何获得一个知识类,并导致更脆的代码),并关注同步,正确的解决办法是使用的静态初始化以实例化实例。

private static Singleton instance = createInst();

public static Singleton getInst() {
    return instance ;
}

private static synchronized createInst() {
    return new Singleton(); 
}

Java语言规范保证静态initiailzers将只是运行一次,当一个类被装载在第一次,并且在保证线程安全的方式。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top