【发布时间】:2010-09-21 19:43:01
【问题描述】:
如果我只使用来自AtomicReference 的get() 和set() 方法,volatile 对象引用和AtomicReference 之间有什么区别吗?
【问题讨论】:
标签: java concurrency
如果我只使用来自AtomicReference 的get() 和set() 方法,volatile 对象引用和AtomicReference 之间有什么区别吗?
【问题讨论】:
标签: java concurrency
简短的回答是:不。
来自java.util.concurrent.atomic 包文档。引用:
atomics 的访问和更新的记忆效应一般遵循 volatile 的规则:
get具有读取volatile变量的记忆效应。set具有写入(分配)volatile变量的记忆效应。
顺便说一句,那个文档非常好,所有的东西都解释了。
AtomicReference::lazySet 是一种较新的 (Java 6+) 操作,其语义无法通过 volatile 变量实现。请参阅this post 了解更多信息。
【讨论】:
不,没有。
AtomicReference 提供的额外功能是 compareAndSet() 方法和朋友。如果您不需要这些方法,可变引用提供与 AtomicReference.set() 和 .get() 相同的语义。
【讨论】:
有几个区别和权衡:
使用 AtomicReference get/set 具有与 volatile 字段相同的 JMM 语义(如 javadoc 所述),但 AtomicReference 是引用的包装器,因此任何对该字段的访问都涉及进一步的指针追逐。
内存占用成倍增加(假设是压缩的 OOP 环境,大多数 VM 都是如此):
AtomicReference = 4b + 16b (12b object header + 4b ref field)AtomicReference 提供了比 volatile 引用更丰富的 API。您可以使用AtomicFieldUpdater 或使用Java 9 的VarHandle 重新获得易失性引用的API。如果您喜欢用剪刀跑步,也可以直接联系sun.misc.Unsafe。 AtomicReference 本身是使用Unsafe 实现的。
那么,什么时候最好选择一个而不是另一个:
AtomicReference/AtomicFieldUpdater/Unsafe 之间进行选择,您往往会为提高性能付出可读性和风险。如果这不是敏感区域,请选择AtomicReference。库编写者通常会根据目标 JDK、预期的 API 限制、内存限制等混合使用这些方法。【讨论】:
JDK source code 是解决此类困惑的最佳方法之一。如果您查看 AtomicReference 中的代码,它使用 volatie 变量进行对象存储。
private volatile V value;
所以,很明显,如果您打算在 AtomicReference 上使用 get() 和 set(),那就像使用 volatile 变量一样。但正如其他读者评论的那样,AtomicReference 提供了额外的 CAS 语义。因此,首先决定是否需要 CAS 语义,如果只需要,则使用 AtomicReference。
【讨论】:
AtomicReference 提供了普通 volatile 变量不提供的附加功能。当您阅读 API Javadoc 时,您会知道这一点,但它还提供了一个锁,对某些操作很有用。
但是,除非您需要此附加功能,否则我建议您使用普通的 volatile 字段。
【讨论】:
volatile 字段可以像任何常规字段一样使用,而访问 AtomicReference 中的值需要通过 get 和 set 方法。
有时即使您只使用gets 和sets,AtomicReference 也可能是一个不错的选择:
易失性示例:
private 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 some called setNewStatus(). setNewStatus should be synchronized
}
}
使用 AtomicReference 的实现将免费为您提供写时复制同步。
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;
但是第二个更有可能在将来“代码清理”期间被某人意外删除。
【讨论】: