创建单例时使用锁定对象

Using Lock Object when creating a singleton

所以在我当前的项目中,我有这样的代码:

private static final Object LOCK_OBJECT = new Object();
    private static Service getEntryPoint() {

    synchronized (LOCK_OBJECT) {
        if (entryPoint != null) {
            return entryPoint;
        }
        return new Service();
    }
}

我的问题是:这种方法和 "classic" 单例方法有什么区别: 在这种情况下,这个锁对象背后的实际想法是什么?

    private static Service initializeEntryPoint() {

    if (entryPoint == null) {
        synchronized (Service.class) {
            if (entryPoint == null) {
                entryPoint = new Service();
            }
        }
    }
    return entryPoint;
}

根据您的问题 1。在这种情况下,这个锁对象背后的实际想法是什么?

  1. 当我们在您的单例 class 中执行 synchronized (Service.class) 时,您可以在其他一些 class 的同步块中使用 Service.class 并调用对于你的单身人士来说,直到同步没有退出,因为 Service.class return 相同的 Class class object.

  2. 另一方面,当您像下面这样写时,这是您的本地 class 并且只有您的 class 可以访问此对象,因此它不受其他同步的影响堵塞。

    Object LOCK_OBJECT = new Object()
    

关于第二个问题为什么加倍 if (entryPoint == null) check.

  1. 在这种情况下,如果有 2 个线程进入你的方法并且都检查 if (entryPoint == null) 那么两者都会一个接一个地进入同步块,你将有 2 个对象违反单例契约。

  2. 当你在 synchronized 块中进行附加检查时,这可以保证即使多个线程进入第一次检查,它们也不会从第二次检查中逃脱。

更多详情请查看:Double Checked Locking in Singleton

最好使用 Enum 来创建你的单例 class 参见 Implementing Singleton with an Enum (in Java):

public enum MySingleton {
     INSTANCE;   
}

在这两种情况下同步背后的想法是为了防止两个线程第一次同时调用getEntryPoint方法。在这种情况下,如果你不进行同步,你最终会得到两个单例对象的实例。

这就是理论。

实际上,第一个版本完全损坏,因为您没有存储创建的服务实例,如果您再次调用它,将创建新的服务实例。

第二个似乎可行,但可能会受到编译器优化的影响(阅读 article

所以最好的办法就是急切初始化。简单有效

public class Singleton {
  public static final Singleton INSTANCE = new Singleton();

  private Singleton() {}

}

值得一提的是,现在像这样实现的单例被认为是反模式,您应该考虑以一种方式组织代码,将这些对象注入到需要它们的代码中(通过一些 DI 容器,如 spring guice 等)或手动。