java - 什么时候单例不是单例?

标签 java design-patterns singleton

我正在阅读 blog其中讨论了当单例不是单例时。

在作者试图解释的一个案例中,展示了双重检查锁定在单例上实现时如何也可能失败。

// Double-checked locking -- don't use
public static MySingleton getInstance() {
    if (_instance==null) {
      synchronized (MySingleton.class) {
        if (_instance==null) {
           _instance = new MySingleton();
        }
      }
    }
}

对于上面的代码块作者说:

“在这种情况下,我们打算避免每次调用方法时都去抢单例类的锁的开销,只有在单例实例不存在的情况下才抢锁,然后实例的存在是再次检查以防另一个线程在当前线程之前通过第一次检查。”

谁能帮我解释一下这到底是什么意思?

最佳答案

我会尽量说清楚。

synchronized block 需要时间才能进入,因为它需要跨线程协调。如果需要,我们将尽量避免输入它。

现在,如果我们使用多线程,如果对象已经存在,我们就返回它,因为方法会在内部针对线程竞争条件进行 self 同步。我们可以在进入同步块(synchronized block)之前执行此操作,就好像它已创建一样。构造函数已经设计好,因此无法返回部分构造的对象,正如内存模型设计所指定的那样。

如果单例对象不存在,我们需要创建一个。但是,如果我们正在检查另一个线程创建了它怎么办?我们将使用 synchronized 来确保没有其他线程持有它。现在,一旦我们进入,我们再次检查。如果单例是由另一个线程创建的,那么让我们返回它,因为它已经存在了。如果我们不这样做,一个线程可能会得到它的单例并对它做一些事情,而我们只会压倒它的变化和影响。

如果没有,让我们锁定它并返回一个新的。通过持有锁,我们现在可以保护单例免受另一端的侵害。另一个线程等待锁,并注意到它已被创建(根据内部 null 比较)返回现有的锁。如果我们没有获得锁,线程就会压倒变化,并发现它们的变化也被破坏了。 请注意,您帖子中的代码块不完整。如果任何空检查返回 false,则需要返回 _instance,使用 else block 。

现在,如果我们处于线程环境中,这将不重要。我们可以使用:

public static MySingleton getInstance() {
    if (_instance==null) {
        _instance = new MySingleton();
    }
    else return _instance;
}

对于较新的版本,java 在许多情况下使用此行为,作为其库的一部分,检查是否需要锁花时间获取它之前。之前,它要么无法获取锁(不好,数据丢失),要么立即获取锁(不好,更有可能导致减速和死锁)。

为了线程安全,您仍然应该在自己的类中自己实现它。

关于java - 什么时候单例不是单例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17386227/

相关文章:

Java 重写类以加载项目类而不是 Jar 类

java - 是否有用于使用体素引擎进行渲染的 Java 库?

java - 默认 Java 类(即非公共(public)、非抽象、非最终、Object 的子类)有什么好处吗?

ios - 单例类加载数据

Java 序列化 : readObject() vs. readResolve()

java - 将 csv 中写入的行数与结果集行数进行比较

design-patterns - 我什么时候应该使用构建器设计模式?

java - 在观察者模式中拥有多个观察者列表的最佳方式

c++ - 为什么这个单例实现不能编译?

java - 在 Java 中递归地创建/解析子级