我试图利用非阻塞原子布尔API生成单例对象而不是同步对象。
我有2个实现
我相信通过Atomic可以比同步实现更健壮和更好的实现。如果我不正确,请提出建议。还对性能进行一些基本测试,这有利于原子实现而不是同步。
支持
// Lazy Initialization
// Thread-safe
// Performance improvement for corresponding calls, once the object is created
public class Singleton {
private static Singleton instance;
static final AtomicBoolean isInitialized = new AtomicBoolean(false);
private Singleton() {
// private constructor //Thread.sleep(10)
}
// 1st Implementation
// Via Double Locking and Synchronized
public static Singleton getInstance() {
if (instance == null) {
// synchronized block to remove overhead
synchronized (Singleton.class) {
if (instance == null) {
// if instance is null, initialize
instance = new Singleton();
}
}
}
return instance;
}
// 2nd Implementation + Not ThreadSafe with Null Objects.
// Via Atomic Non Blocking Method and avoiding Synchronized
public static Singleton getInstanceViaAtomic() {
if (instance != null)
return instance;
if (isInitialized.compareAndSet(false, true)) {
return instance = new Singleton();
}
return instance;
}
}
更新 @Kayaman正确识别出上述暗示不是线程安全的。我固定在下一个。
// 2nd Implementation + Thread Safe
// Via Atomic Non Blocking Method and avoiding Synchronized
public static Singleton getInstanceViaAtomic() throws InterruptedException {
while(instance == null) {
if (isInitialized.compareAndSet(false, true)) {
instance = new Singleton();
}
}
return instance;
}
第二个实现不是线程安全的。一种简单的显示方法是将Thread.sleep(10);
(或更多)放入Singleton
的构造函数中。
[这导致第一个线程将isInitialized
设置为true
(instance
仍然为空时,然后调用new Singleton();
。
[另一个线程将instance
视为null
,它不会进入if
块,因为布尔值现在是true
,然后它将返回instance
,该值为空。
所以竞争条件以NullPointerException
结尾。
如果要强制将此解决方案变为可行的解决方案,则必须使用自旋锁,并且可能会发生类似这样的事情(这是早晨代码,所以请让我知道是否有奇怪的事情:]]]
public static Singleton getInstance() { // Fast-path when singleton already constructed if(instance != null) return instance; // Spinlock lets the first thread through, others will spin while(instance == null && !isInitialized.compareAndSet(false, true)) ; // First thread creates the singleton, spun threads will ignore if(instance == null) instance = new Singleton(); return instance; }
instance
也必须为volatile
以确保可见性。
进入的第一个锁将清除自旋锁,因为即使实例为null,!compareAndSet
也会返回false(因为它第一次成功)。
此后,进入的任何线程都将保留在自旋锁中,因为instance == null
和!compareAndSet
为true
。实例构造完成后,由于第一个条件,自旋锁将始终掉线。
这基本上是具有自旋锁的DCL,而不是同步的,而且我认为上面的代码在任何情况下都不会有用。