2

我正在阅读一篇讨论单例何时不是单例的博客。

在作者试图解释的一种情况下,在 Singleton 上实现双重检查锁定也可能会失败。

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

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

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

有人可以帮我解释这到底是什么意思吗?

4

3 回答 3

4

我会试着说清楚。

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

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

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

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

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

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

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

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

于 2013-06-30T01:28:45.033 回答
3

没有解释那句话是如何失败的。他只是在解释双重检查锁定。他可能在别处提到双重检查锁定本身在 Java 1.5 之前不起作用的事实。但那是很久以前的事了。

于 2013-06-30T01:28:23.123 回答
1

我在维基百科上找到了对不同单例实现的最佳解释、它们的缺陷以及什么是最好的。按照这个链接:

http://en.wikipedia.org/wiki/Singleton_pattern

希望能帮助到你!

于 2013-06-30T01:28:32.797 回答