这个Singleton是一个线程安全的吗?

时间:2012-09-19 13:21:02

标签: java multithreading concurrency singleton atomicity

基于this topic,我提出了一个有趣的Singleton模式版本,该实现基于AtomicIntegers。

问题是:

  • 这个实现是正确的和线程安全的,通常可以使用Atomic Variables进行线程同步和管理吗?
  • 其他问题:如果此实现是线程安全的,我是否真的需要volatile修饰符作为实例变量?
public class StrangeSingleton
{

    private StrangeSingleton() {};

    private static volatile Object instance;

    private static AtomicInteger initCounter = new AtomicInteger();
    private static AtomicInteger readyCounter = new AtomicInteger();

    static Object getInstance()
    {

        if (initCounter.incrementAndGet() == 1)
        {
            instance = new Object();

            readyCounter.incrementAndGet();

            return instance;
        }
        else if (readyCounter.get() == 1)
        {
            return instance;
        }
        else
        {
            //initialization not complete yet.
            //write here some logic you want:               
            //sleep for 5s and try one more time,
            //or throw Exception, or return null..

            return null;
        }
    }
}

UPDATE:添加了私有构造函数,但不是重点。

4 个答案:

答案 0 :(得分:8)

  

这个实现是否正确且是线程安全的,通常可以使用Atomic Variables进行线程同步和管理?

但是它通常更加复杂和CPU密集,因为你需要忙着等待快速响应变化。

  

补充问题:如果这个实现是线程安全的,我真的需要一个volatile修饰符作为实例变量吗?

在这种情况下,您不会这样做,因为AtomicInteger包含易失性字段,这些字段将确保正确发生在事件之前/发生之后的行为。


当然你可以使用一个线程安全且更简单的枚举;)

enum Singleton {
    INSTANCE;
}

答案 1 :(得分:2)

  

这个实现是否正确且是线程安全的,并且通常可以使用Atomic Variables进行线程同步和管理?

是的,但对于readyCounter变量,您应该使用CountDownLatch,如下所示:

private static AtomicInteger initCounter = new AtomicInteger();
private static CountDownLatch readyCounter = new CountDownLatch(1);

static Object getInstance()
{

    if (initCounter.incrementAndGet() == 1)
    {
        try {
            instance = new Object();
            return instance;
        } finally {
            readyCounter.countDown();
        }
    }
    else
    {
        readyCounter.await();
        return instance;
    }
}

调用 await()也可以解决初始化竞争条件。 (我还添加了一个try-finally块来避免构造函数异常死锁。)

  

补充问题:如果这个实现是线程安全的,我真的需要一个volatile修饰符作为实例变量吗?

不,如果在访问实例变量之前调用相关的AtomicIntegerCountDownLatch函数,则不行。在documentation中查找发生在之前。

答案 2 :(得分:0)

线程 T1 可能会在instance = new Object();暂停, T2 会在else{}阻止后readyCounter阻止增加了。 Thtat不太正确,因为初始化已经完成,落后的是状态簿记

答案 3 :(得分:0)

我宁愿在synchronized方法中执行一次getInstance()阻止。这就足够了。你不需要这些奇怪的计数器,这些计数器也不像@David注意到的那么安全。