什么时候适合使用volatile
原语(例如boolean
, integer
or long
)而不是AtomicBoolean
, AtomicInteger
or AtomicLong
,反之亦然?
4 回答
可见性语义完全相同,使用原子原语的情况是当您需要使用它们的原子方法时。
例如:
if (volatileBoolean) {
volatileBoolean = !volatileBoolean;
}
可能会在多线程环境中产生问题,因为变量可能会在两行之间发生变化。如果您需要测试和分配是原子的,您可以使用:
atomicBoolean.compareAndSet(true, false);
如果您只想设置或获取值,则使用普通的 volatile 会更简单、更有效。(但不能获取&设置值)
如果您想要更多操作,例如 getAndIncrement 或 compareAndSwap,您需要使用 AtomicXxxx 类。
这些Atomic*
类包装了volatile
相同类型的原语。从来源:
public class AtomicLong extends Number implements java.io.Serializable {
...
private volatile long value;
...
public final long get() {
return value;
}
...
public final void set(long newValue) {
value = newValue;
}
所以。
何时适合使用 volatile 原语(例如 boolean、integer 或 long)而不是 AtomicBoolean、AtomicInteger 或 AtomicLong
如果您所做的只是获取和设置 aAtomic*
那么您还不如只使用一个volatile
字段。
……反之亦然?
然而,这些Atomic*
类为您提供的是提供更高级功能的方法,例如incrementAndGet()
、compareAndSet()
和其他实现多个操作(get/increment/set、test/set)而无需锁定的方法。这就是Atomic*
类如此强大的原因。
同样重要的是要注意,volatile
使用类包装字段Atomic*
是从对象角度封装关键共享资源的好方法。这意味着开发人员不能只处理该字段,假设它不共享,可能会field++;
在引入竞争条件的代码或其他代码中注入问题。
我们已经开始禁止volatile
在我们的源代码中使用,因为很容易编写并不总是按预期工作的代码。
根据我的经验,人们添加 volatile 以在线程之间共享值。然后,其他人开始修改该值。大多数时候,这是有效的。但是在生产中,您会开始遇到难以追踪的奇怪错误。计数器增加 100'000 次(测试只增加 10 次)但最终达到 99'997。在 Java 1.4 中,长值可能会被破坏,非常非常罕见。
Atomic*
另一方面,助手类只产生很小的开销,而且它们总是像宣传的那样工作。
所以除非你有很好的理由(*)来使用volatile
,否则总是更喜欢Atomic*
辅助类。
如果您不确切知道Atomic*
辅助类中的每个字符的作用,那么您真的应该避免使用volatile
.
*:过早的优化从来都不是一个好的理由。