【问题标题】:Notifying all the waiting threads in case the instance is created in case of singleton double lock check在单例双锁检查的情况下创建实例时通知所有等待线程
【发布时间】:2013-02-27 06:47:53
【问题描述】:
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance; //4
}
在上面的代码中,假设有 10 个线程正在调用这个方法,它们都超过了第一个 if 条件,然后一个线程进入同步块并创建实例。即使创建了实例,剩余的 9 个线程也应该一个接一个地到来,它们需要经过同步块,一旦任何线程创建了 Singleton 实例,所有其他线程都不应该等待。告诉我是否有解决方案?
【问题讨论】:
标签:
java
design-patterns
singleton
【解决方案1】:
考虑到 Singleton 不是非常复杂的结构化类或者在构造函数的初始化中没有太多需要加载的东西,我认为它的代码很好,阻塞时间不会那么长。如果所有线程同时到达,则它们必须等到任何一个线程创建第一个对象。
【解决方案2】:
没有条件同步。这意味着每当您使用同步时,它都会一直强制执行。
在上面的示例中,当然可以选择在静态块中初始化 Singleton,然后您不需要 getter 的同步块。
【解决方案3】:
你是说你希望所有其他九个线程都运行,而不是一次获得一个锁。
首先我不得不提一下,等待初始化后获取锁的性能问题很小。其次,单例是邪恶的,有更好的方法来实现它们。
但是,如果您想部分地重新实现锁,那么您可以这样做。这样做可能有一些现成的Future,但我什么都看不到。无论如何,糟糕的实施让我一头雾水:
private static final AtomicReference<Object> ref = new AtomicReference<>();
// <Object> as we require two distinguished values. :(
// (I guess could use a mark.)
public static Singleton getInstance() {
for (;;) { // May want to play with park to avoid spinning.
Object obj = ref.get();
if (obj instanceof Singleton) {
return (Singleton)obj;
}
if (ref.weakCompareAndSet(null, Thread.currentThread())) {
Singleton instance = null; // To reset on fail.
try {
instance = new Singleton();
} finally {
ref.set(instance);
}
return instance;
}
}
}
我想我们可以做得稍微好一点而不会变得太复杂,再次假设没有例外:
{
Object obj = ref.get();
if (obj instanceof Singleton) {
return (Singleton)obj;
}
}
if (ref.compareAndSet(null, Thread.currentThread())) {
Singleton instance = new Singleton();
ref.set(instance);
return instance;
}
for (;;) {
Object obj = ref.get();
if (obj instanceof Singleton) {
return (Singleton)obj;
}
}
【解决方案4】:
按照您的设计,剩余的线程确实必须一次通过一个线程 - 实际上对于任何像这样的锁定策略,您都会得到相同的结果。也就是说,现在锁定的成本微不足道。
请注意,尽管未显示,但 instance 变量必须为 volatile 才能使此习惯用法起作用。
如果您想要一种模式来满足您的需求(以低成本进行延迟初始化),那么我建议您使用静态初始化方法(如 Brian Goetz 所推荐的):
public class Singleton {
static class Holder {
static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
这种方法简洁整洁,并且也是线程安全的,在您的情况下,访问静态 getInstance 方法(或任何静态方法)的第一个线程将支付初始化成本,但所有后续线程都不会,也不会阻塞。