基于this topic,我提出了一个有趣的Singleton模式版本,该实现基于AtomicIntegers。
问题是:
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:添加了私有构造函数,但不是重点。
答案 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修饰符作为实例变量吗?
不,如果在访问实例变量之前调用相关的AtomicInteger
或CountDownLatch
函数,则不行。在documentation中查找发生在之前。
答案 2 :(得分:0)
线程 T1 可能会在instance = new Object();
暂停, T2 会在else{}
阻止后readyCounter
阻止增加了。 Thtat不太正确,因为初始化已经完成,落后的是状态簿记
答案 3 :(得分:0)
我宁愿在synchronized
方法中执行一次getInstance()
阻止。这就足够了。你不需要这些奇怪的计数器,这些计数器也不像@David注意到的那么安全。