我正在阅读 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/