如果在单例双锁检查的情况下创建实例,则通知所有等待的线程

时间:2013-02-27 06:47:53

标签: java design-patterns singleton

public static Singleton getInstance()
{ 
if (instance == null)
    {
    synchronized(Singleton.class) {  //1
    if (instance == null)          //2
    instance = new Singleton();  //3
        }
    }
    return instance; //4
} 

在上面的代码中,假设有10个线程正在调用此方法,所有这些线程都超过了第一个if条件,然后一个线程进入synchronized块并创建实例。即使创建了实例,他们也需要经过同步块,剩下的9个线程应该逐个到来。只要任何线程创建了Singleton实例,所有其他线程就不应该等待。告诉我是否有解决方案吗?

4 个答案:

答案 0 :(得分:0)

我认为它的良好代码和阻塞时间不会那么多,因为Singleton不是非常复杂的结构化类,或者在构造函数的初始化中没有太多东西需要加载。如果所有线程同时出现,则必须等到任何一个线程创建第一个对象。

答案 1 :(得分:0)

没有条件同步。这意味着无论何时使用synchronized,都会在整个时间内强制执行。 在上面的例子中,它当然是一个在静态块中初始化Singleton的选项,然后你不需要getter的synchronized块。

答案 2 :(得分:0)

你是说你希望所有其他九个线程都能在不获取锁定的情况下运行。

首先,我觉得有必要提一下,等待初始化后获取锁的性能问题很小。其次,单身人士是邪恶的,有更好的方法来实施它们。

但是,如果要部分重新实现锁定,则可以执行此操作。可能有一些现成的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;
        }
    }

答案 3 :(得分:0)

正如你的设计一样,剩下的线程确实必须一次经过一个线程 - 而且对于任何这样的锁定策略你都会得到相同的结果。也就是说,现在锁定的成本微不足道。

请注意,虽然未显示,但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方法(或任何静态方法)的线程将支付初始化的成本,但所有后续的不会,也不会阻止。