【问题标题】:Is this code correct example of thread safe Singleton design pattern?此代码是线程安全单例设计模式的正确示例吗?
【发布时间】:2018-02-12 12:14:02
【问题描述】:

下面的代码是 Java 中线程安全单例模式的正确示例吗?

    class Singleton {
    private static Singleton INSTANCE = new Singleton();

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

由于静态成员只初始化一次,在JVM加载类时保证是线程安全的,所以这不是线程安全Singleton类(早期加载)的上述代码示例吗?

上面的说法有什么问题吗?如果是,那么将其转换为线程安全的 Singleton 类需要进行哪些最小更改?

【问题讨论】:

  • 它是完全线程安全的。
  • 您在有关初始化此类的声明中是正确的,但是实例不是最终的,因此当您将另一个对象分配给变量时可能会发生一些无法预料的行为。
  • 谢谢维克兰特。第一个链接仅指延迟初始化示例,其中第二个链接提到了确切的内容

标签: java multithreading singleton


【解决方案1】:

JLS 保证这是安全的。见the holder pattern:“由于JLS保证类初始化阶段是顺序的,即非并发的,所以在加载和初始化过程中,静态getInstance方法不需要进一步同步。”

持有人模式比你想要的更复杂,但重要的部分是static final Something INSTANCE = new Something()无论在哪个类中声明都是安全的。持有人模式与你所拥有的相比的好处是单例获胜'直到第一次使用时才被初始化。如果您想在初始化 Singleton 实例的成本很高时访问 Singleton 类中的其他静态成员,这将很有帮助。

正如Lewis_McReuuser6690200 指出的那样,您应该声明INSTANCE 字段final,以确保您不会意外地将不同的Singleton 实例分配给变量。您还应该声明一个私有无参数 Singleton() 构造函数,以防止创建其他实例。为了使其更加防弹,您应该声明 Singletonfinal 以便您不能使用 public 构造函数对其进行子类化。

【讨论】:

  • 您在链接中引用了按需初始化持有人成语。在 OP 代码中并不懒惰。
  • 重要的部分是static final Something INSTANCE = new Something() 是安全的,无论它在哪个类中声明。
  • 即使不是final,就初始化而言也是线程安全的。然而,这在其他方面很危险。
  • 所有单例引用都应该声明为final,因为根据定义应该只创建一个实例,因此保留重新分配引用的能力是没有意义的(除非你想有条件地@987654337 @它在未来的某个时间点发布)。
  • 回答您的问题,它不会改变您发布的代码的行为。但是,标记字段final 将防止您意外写入void setInstance(Singleton o) { INSTANCE = o; } 之类的内容,这可能是线程不安全的,并且由于其他原因也是非常错误的。
猜你喜欢
  • 2014-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-08
  • 1970-01-01
  • 2011-09-19
  • 2021-05-12
  • 1970-01-01
相关资源
最近更新 更多