如果我只使用AtomicReference
中的get()
和set()
方法,那么volatile
对象引用和AtomicReference
之间有什么区别吗?
如果我只使用AtomicReference
中的get()
和set()
方法,那么volatile
对象引用和AtomicReference
之间有什么区别吗?
简短回答是:不可以。
根据java.util.concurrent.atomic包的文档,引用如下:
对于原子访问和更新的内存效果通常遵循volatile变量的规则:
get
具有读取volatile
变量的内存效果。set
具有写入(分配)volatile
变量的内存效果。
顺便说一句,这个文档非常好,所有内容都有解释。
AtomicReference::lazySet
是一个更新的(Java 6+)操作,它具有通过volatile
变量无法实现的语义。请参考此帖子获取更多信息。
没有。
AtomicReference 提供的额外功能是 compareAndSet() 方法及其相关方法。如果您不需要这些方法,一个 volatile reference 可以提供与 AtomicReference.set() 和 .get() 相同的语义。
有几个不同点和权衡:
使用 AtomicReference
的 get/set 与 volatile 字段具有相同的 JMM 语义(如 javadoc 所述),但 AtomicReference
是一个引用的包装器,因此任何对字段的访问都涉及进一步的指针跟踪。
内存占用增加(假设为压缩 OOPs 环境,这对大多数 VM 都是正确的):
AtomicReference
= 4b + 16b(12b 对象头 + 4b 引用字段)AtomicReference
提供了比 volatile 引用更丰富的 API。您可以通过使用 AtomicFieldUpdater
或 Java 9 中的 VarHandle
来恢复 volatile 引用的 API。如果您喜欢运行剪刀,也可以直接使用 sun.misc.Unsafe
。 AtomicReference
本身是使用 Unsafe
实现的。那么,什么时候选择其中一个比另一个好呢:
AtomicReference
/AtomicFieldUpdater
/Unsafe
之间做出选择,其中您往往会为了性能而付出可读性和风险。如果这不是敏感区域,只需选择AtomicReference
即可。库编写者通常根据目标JDK、预期API限制、内存限制等使用这些方法的组合。private volatile V value;
显然,如果你只是使用AtomicReference的get()和set()方法,那就像使用一个volatile变量。但正如其他读者所评论的,AtomicReference提供了额外的CAS语义。因此,首先确定是否需要CAS语义,只有在需要时才使用AtomicReference。
AtomicReference
提供了比普通的 volatile 变量更多的功能。当您阅读 API Javadoc 时,您会知道这一点,但它还提供了一个锁,对于某些操作非常有用。
然而,除非您需要此额外功能,否则建议使用普通的 volatile
字段。
volatile
字段可以像任何常规字段一样使用,而访问 AtomicReference
中的值需要通过 get
和 set
方法。 - David Harknessprivate volatile Status status;
...
public setNewStatus(Status newStatus){
status = newStatus;
}
public void doSomethingConditionally() {
if(status.isOk()){
System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime someone called setNewStatus(). setNewStatus should be synchronized
}
}
private AtomicReference<Status> statusWrapper;
...
public void doSomethingConditionally() {
Status status = statusWrapper.get();
if(status.isOk()){
System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
}
}
有人可能会说,如果您替换了以下内容,仍然可以拥有一个适当的副本:
Status status = statusWrapper.get();
使用:
Status statusCopy = status;
然而,我猜第二个更可能在未来的“代码清理”中被某人意外删除。
status
的值在 if
测试和 println
语句中使用之间可能会发生变化。我不确定在清理期间哪个版本更有可能被移除。 - LordOfThePigs