2012-09-19 51 views
4

基于this topic,我放下了一个有趣的Singleton模式,其实现基于AtomicIntegers。这个Singleton是一个线程安全的吗?

的问题是:

  • 这是实现正确的,线程安全的,一般是有可能使用原子变量进行线程同步和管理?
  • 其他问题:如果这个实现是线程安全的,我真的需要一个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; 
     } 
    } 
} 

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

+2

您错过了私有构造函数! – Santosh

+0

当有人可以调用'new StrangeSingleton()'时,它怎么可能是单例。创建一个不带任何参数的私有构造函数。 – km1

+2

你可以使用'AtomicBoolean'而不是'AtomicInteger'。使用'compareAndSet()'。 – Gray

回答

8

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

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

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

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


当然,你可以只使用一个枚举类型,是线程安全和更简单;)

enum Singleton { 
    INSTANCE; 
} 
+0

彼得,这与通常的双重检查锁定机制(从性能角度来看)相比如何? – Santosh

+0

双重检查锁定会比检查两个AtomicXxxxx更快。使用'enum'是最快的,因为它不需要一个易失字段并且不需要检查。 –

+1

@Santosh由于某些实现语义的原因,在1.4及更早版本的所有平台上双锁也不一定安全。 ([Wikipedia article。](http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java))这在5+中不是问题。还有,+1 – Brian

2

这是实现正确的,线程安全的,一般是有可能使用原子线程同步和管理的变量?

是的,但对于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; 
    } 
} 

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

其他问题:如果此实现是线程安全的,那么是否真的需要一个volatile变量来指定实例变量?

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

+0

Latch的大odea,谢谢,Soulman! – KutaBeach

0

螺纹T1可悬浮于instance = new Object();,然后T2将击中else{}块自readyCounter尚未递增。 Thtat是不太正确的,因为初始化完成,什么是落后的状态簿记

0

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

相关问题