给出以下代码,其中我在运行某些代码之前检查 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
}
}
}
最佳答案
您的两个示例之间有两个重要区别。
首先,很明显:第一个示例中的 doInit()
方法仅设置 isInitialized=true
after 初始化单例。但是,在您的第二个示例中,它将 AtomicBoolean
实例设置为 true
before 它初始化单例。如果第二个线程在设置标志之后但在初始化完成之前获得对单例的引用,则可能会出现问题。
第二个问题不太明显:在你的第二个例子中设置标志后没有同步。在初始化代码和其他线程中发生的任何事情之间没有建立发生在之间的关系。在您的第一个示例中,初始化发生在 isInitialized=true
之前,并且isInitialized=true
发生在 任何其他线程之前测试 if(!isInitialized)...
在你的第二个例子中,如果两个线程同时调用doInit()
,Atomic操作确保只有其中一个可以进入初始化代码。但是,即使获胜者(纯属偶然)实际上在*另一个线程开始使用单例对象之前完成了初始化代码,在执行初始化的第一个线程和执行初始化的第一个线程之间仍然没有正式的先于关系第二个线程访问单例。
* 实时“之前”(又名挂钟时间)。如果某个事件 A 实际上实时发生在事件 B 之前,但是两个事件之间没有正式的发生在关系,那么其他线程可能会看到这两个事件的影响好像它们以不同的顺序发生。
关于java - AtomicBoolean 可以替换 volatile boolean 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69806119/