【问题标题】:Can AtomicBoolean replace volatile boolean?AtomicBoolean 可以替换 volatile 布尔值吗?
【发布时间】:2021-11-02 06:21:42
【问题描述】:

给定以下代码,我在其中检查布尔值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            
        }
    }

}

【问题讨论】:

    标签: java concurrency


    【解决方案1】:

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

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

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

    之前

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


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

    【讨论】:

    • 用简单的英语:第二个变体非常糟糕。我喜欢你的回答。人们似乎没有得到(一点)volatile 只是 DCL 中的优化,它不是强制性的
    • 在第一个例子中,如果我使用没有 volatile 的 AtomicBoolean,我想它应该仍然可以正常工作,因为“同步”也会强制发生之前?
    • @wayne 显示一个实际示例来说明您的意思,然后我们可以对其进行推理,用这类事情猜测是非常危险的。即使您最初的示例也不是那么好,您从不显示谁是读者,初始化了什么等等。
    • @Eugene 更多代码在这里app.codingrooms.com/w/Qp3bwUTSKv7w。真正的用例是我想在代码中初始化一些缓存。
    【解决方案2】:

    是的,它可以。可见性是保证。

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

    细微的差别是同步时不能确定多个线程之间的执行顺序。

    【讨论】:

      【解决方案3】:

      AtomicXxxx 封装了一个 volatile,所以它们基本相同,不同的是它提供了更高级别的操作,例如用于实现增量的 CompareAndSwap。

      【讨论】:

        猜你喜欢
        • 2013-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-26
        • 2019-10-15
        • 1970-01-01
        • 1970-01-01
        • 2013-05-20
        相关资源
        最近更新 更多