使用原子非阻塞方法的单例与同步
Singleton using Atomic Non Blocking Method vs Synchornized
我尝试使用非阻塞原子布尔值 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;
}
}
Update @Kayaman 正确识别了上述 impl 不是线程安全的。我在下面修复了。
// 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;
}
第二个实现不是线程安全的。显示它的一种简单方法是在 Singleton
.
的构造函数中放置一个 Thread.sleep(10);
(或更多)
这会导致第一个线程将 isInitialized
设置为 true
(而 instance
仍然为空),然后调用 new Singleton();
.
另一个线程会将 instance
视为 null
,它不会进入 if
块,因为布尔值现在是 true
,然后它将 return 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
也会 return false(因为它是第一次成功)。
在此之后,任何进入的线程都将留在自旋锁中,因为 instance == null
和 !compareAndSet
是 true
。当实例构造完成后,由于第一个条件,自旋锁总是落空。
这基本上是带有自旋锁而不是同步的 DCL,我认为上面的代码没有任何用处。
我尝试使用非阻塞原子布尔值 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;
}
}
Update @Kayaman 正确识别了上述 impl 不是线程安全的。我在下面修复了。
// 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;
}
第二个实现不是线程安全的。显示它的一种简单方法是在 Singleton
.
Thread.sleep(10);
(或更多)
这会导致第一个线程将 isInitialized
设置为 true
(而 instance
仍然为空),然后调用 new Singleton();
.
另一个线程会将 instance
视为 null
,它不会进入 if
块,因为布尔值现在是 true
,然后它将 return 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
也会 return false(因为它是第一次成功)。
在此之后,任何进入的线程都将留在自旋锁中,因为 instance == null
和 !compareAndSet
是 true
。当实例构造完成后,由于第一个条件,自旋锁总是落空。
这基本上是带有自旋锁而不是同步的 DCL,我认为上面的代码没有任何用处。