我的理解:声明变量volatile
保证其他线程对该变量的写入的可见性。从本质上讲,对易失变量的每个write
都发生在后续reads
之前。
我了解AtomicBoolean.compareAndSet()
的原子性以及它如何提供volatile
没有read+write
操作的原子性。但是我没有看到任何文档通过如下所示AtomicBoolean
提供可见性保证:
- 每个成功的
write
byAtomicBoolean.compareAndSet()
最终都会对后续AtomicBoolean.get()
可见,并由其他线程AtomicBoolean.compareAndSet()
。
但是,我一直看到标记为thread-safe
的代码是这样的,
// default false so that first-thread that execute() can enter the logic block
private static final AtomicBoolean executing = new AtomicBoolean(false);
public void execute() {
if (executing.compareAndSet(false, true)) { // check if the executing is previously false and if so update it to true
try {
// thead-safe code, i.e only one thread guaranteed to execute at any point of time time
} finally {
executing.set(false); // executing thread now re-sets the test value
}
}
}
变量executing
不应该也像private static volatile AtomicBoolean executing = new AtomicBoolean(false);
一样声明为volatile
吗?那么AtomicBoolean
所需的能见度保证是否实现了?
是否有必要使
AtomicBoolean
也volatile
?
不。
在示例中,executing
被声明为static final
,因此它将在类初始化时初始化一次,并安全地发布到任何其他需要它的代码。
此行为是有保证的,因为在类初始化完成(正常(和类声明的任何静态变量的任何后续使用之间有一个先行。变量也final
排除了对静态的任何后续赋值,这些赋值会否定之前发生的情况。
您只需要将executing
声明为volatile
,如果某些东西可以在初始化后为其分配新值。 如果不做一些令人讨厌的反思,这是不可能的。 (JLS指出,如果你做这种事情来改变final
,内存模型保证不适用。
如果executing
final
只是实例字段而不是static
字段,您将获得类似的效果。 推理略有不同,但在 JLS 中也明确提到。
最后,Java 语法不允许你组合volatile
和final
修饰符。 这种组合没有意义。
我们不能使用以下代码
private static volatile final AtomicBoolean executing = new AtomicBoolean(false);
易失性和最终性一起使用是无效的。正如@RealSkeptic var 所说,永远不会改变(最终(的变量不需要具有波动性。易失性用于那些其值在运行时被一个或多个线程更改的变量。
快乐学习