0

我尝试使用非阻塞原子布尔 API 来生成单例对象而不是同步对象。

我有 2 个实现

  1. 通过双重锁定和同步关键字
  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 正确识别出上述 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;

}
4

1 回答 1

1

第二个实现不是线程安全的。显示它的一种简单方法是Thread.sleep(10);Singleton.

这会导致第一个线程设置isInitializedtrue(虽然instance仍然为空),然后调用new Singleton();.

另一个线程将看到instancenull它不会进入if块,因为布尔值 now true,然后它将返回instancenull 。

所以一个以 a 结尾的竞争条件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保证能见度。

第一个进入的锁将清除自旋锁,因为即使 instance 为 null,!compareAndSet也会返回 false(因为它第一次成功)。

在此之后,任何进入的线程都将留在自旋锁中,因为instance == null!compareAndSetis true。当实例构造完成时,自旋锁总是会因为第一个条件而失败。

这基本上是带有自旋锁而不是同步的 DCL,并且我认为上面的代码没有任何用处。

于 2019-10-14T17:06:28.987 回答