【问题标题】:Singleton with static inner class and double-checked locking具有静态内部类和双重检查锁定的单例
【发布时间】:2016-04-03 10:28:25
【问题描述】:

我正在阅读单例设计模式并评估不同的实现。我对以下实现有疑问:

A.带有静态内部类的单例实现

public class SingletonWithStaticClass {

private SingletonWithStaticClass(){}

private static class SingletonInnerClass{
    public static SingletonWithStaticClass INSTANCE = new SingletonWithStaticClass();

}

public static SingletonWithStaticClass getInstance(){
    return SingletonInnerClass.INSTANCE;
}

}

B.单例双重检查锁定

public class SingletonWithDoubleCheck {
private static SingletonWithDoubleCheck INSTANCE = null;

private SingletonWithDoubleCheck(){
    if(INSTANCE != null){
        throw new RuntimeException("Accessing private constructor is prohibited. Use getInstance method instead");
    }
}

public static SingletonWithDoubleCheck getInstance(){
    if(INSTANCE == null){
        synchronized (SingletonWithDoubleCheck.class) {
            if(INSTANCE == null){
                INSTANCE = new SingletonWithDoubleCheck();
            }
        }
    }
    return INSTANCE;
}

}

哪个更好?

我觉得我们可以在第一个实现中使用反射访问私有构造函数,而第二个实现是安全的(来自反射攻击)。

但是,我不会在我的生产代码中使用这些,而是​​使用 enum。但是在这两个中,当考虑反射攻击时,不是第一个实现被破坏了吗?

如果我的理解有误,请纠正我。

【问题讨论】:

  • 单例设计模式通常不是为了承受某种蓄意的“攻击”。如果有人使用反射来公开私有构造函数,则他们有责任知道他们所做的事情是否违反了代码的预期用途。
  • 第二个是不安全的,如已发布。这已经被散列和重新散列。我完全同意凯尔伍德。单例是一种设计(反)模式。不是安全工具。如果有人可以访问您的代码,他可以使用反射做任何事情。
  • @JBNizet AFAIK 如果您使用 volatile 字段,它已在 Java 5.0 中修复,但它表明它容易出错。
  • 考虑在 99% 的情况下避免使用单例:stackoverflow.com/questions/137975/…
  • @PeterLawrey 是的,具有易失性。但正如发布的那样,它是不安全的。事实上,它很容易搞砸,应该避免,尤其是当有更好的选择时。一般来说,应该避免使用单例。

标签: java design-patterns singleton


【解决方案1】:

两者都过于复杂。第一个是 Java 5.0 之前的最佳选择,但是第二个从来都不是一个好的选择。在 Java 5.0 之前它不起作用,它需要一个 volatile 字段,在这个版本之后你可以使用 enum

我更喜欢使用enum 来定义一个只有一个实例的类。它是一个final 类,线程安全,延迟加载,并有一个private 构造函数。

enum Singleon {
    INSTANCE;
}

双锁单例只有在你必须在它的构造中提供配置信息时才有用。在这种情况下,我更喜欢使用依赖注入来提供/配置单例,并且只有不需要配置的无状态单例。

【讨论】:

    【解决方案2】:

    两者都不好 - 尝试使用不同的尺度比较它们会适得其反。

    第一个是不必要的复杂,正如你所说,它对反射黑客持开放态度,但另一个也是如此,只是稍微不那么复杂。

    第二个使用synchronized,这是有代价的。

    【讨论】:

    • 任何延迟加载的解决方案都需要某种锁。使用final 字段意味着初始化对象后没有成本(甚至没有检查)。
    猜你喜欢
    • 2013-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多