繁体   English   中英

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

[英]Is this Singleton a thread-safe one?

基于这个主题 ,我提出了一个有趣的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;
        }
    }
}

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

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

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

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

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


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

enum Singleton {
    INSTANCE;
}

这个实现是正确的和线程安全的,通常可以使用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函数,则不会。 文档中寻找发生的事情

线程T1可以挂在instance = new Object(); T2将会触及else{}块,因为readyCounter尚未增加。 由于初始化已经完成,Thtat不太正确,落后的是状态簿记

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM