AtomicBoolean 可以替换 volatile boolean 吗?

Can AtomicBoolean replace volatile boolean?

给定以下代码,其中我在 运行 一些代码之前检查布尔值 isInitialised。如果不是
private static volatile boolean isInitialised;,我用
private static final AtomicBoolean isInitialised = new AtomicBoolean(false);,
它应该达到相同的结果,甚至性能差异可以忽略不计?

public class DclSingleton {

    private static volatile boolean isInitialised;
    //private static final AtomicBoolean isInitialised = new AtomicBoolean(false);

    public static void doInit() {
        if (!isInitialised) {
            synchronized (DclSingleton.class) {
                if (!isInitialised) {
                    // do init
                    
                    isInitialised = true;
                }
            }
        }
    }

}

如果我使用 AtomicBoolean,下面的代码会达到同样的效果吗?

public class DclSingleton {

    // private static volatile boolean isInitialised;
    private static final AtomicBoolean isInitialised = new AtomicBoolean(false);

    public static void doInit() {
        if (isInitialised.compareAndSet(false, true)) {
            // do init            
        }
    }

}

可以。能见度是保证。

AtomicBoolean 类似于同步,但由于 CPU 缓存的 compareAndSwap 实用程序,它被认为优于同步。

细微的区别是它不能确定多个线程之间的执行顺序,而同步可以。

AtomicXxxx封装了一个volatile所以基本相同,不同的是它提供了更高级的操作,比如CompareAndSwap,用来实现increment

你的两个例子有两个重要的区别。

首先,很明显:第一个示例中的 doInit() 方法仅设置 isInitialized=true after 初始化单例。但是,在您的第二个示例中,它将 AtomicBoolean 实例设置为 true 初始化单例之前。如果第二个线程在设置标志后但在初始化完成之前获得对单例的引用,则可能会出现问题。

第二个问题不太明显:在你的第二个例子中设置标志后没有同步。在初始化代码和其他线程中发生的任何事情之间,没有任何东西建立 happens before 关系。在您的第一个示例中,初始化 发生在 isInitialized=true 之前,并且 isInitialized=true 发生在 任何其他线程测试之前 if(!isInitialized)...

在你的第二个例子中,如果两个线程同时调用doInit(),Atomic操作确保只有其中一个可以进入初始化代码。但是即使获胜者,纯粹是偶然,实际上在*另一个线程开始使用单例对象之前完成了初始化代码,第一个线程之间仍然没有正式的happens before关系初始化和第二个线程访问单例。


* 实时中的“之前”(a.k.a,挂钟时间)。如果某个事件A实际上实时发生在事件B之前,但是两个事件之间没有正式的发生在之前的关系,那么有可能让其他线程查看这两个事件的影响好像它们以不同的顺序发生。