如果我只使用volatile
和AtomicReference
- 来自get()
的方法,set()
对象引用与AtomicReference
之间是否有任何区别?
答案 0 :(得分:104)
简短回答是:否。
来自java.util.concurrent.atomic package doc:
原子的访问和更新的记忆效应通常遵循挥发性规则:
get
具有阅读volatile
变量的记忆效应。set
具有编写(分配)volatile
变量的记忆效应。
顺便说一下,包的文档很好,一切都解释了......
lazySet
(在Java 6中引入)是一个引入的新操作,它具有通过volatile
变量无法实现的语义;有关详细信息,请参阅this post。
答案 1 :(得分:40)
不,没有。
AtomicReference提供的额外功能是compareAndSet()方法和朋友。如果您不需要这些方法,则volatile引用提供与AtomicReference.set()和.get()相同的语义。
答案 2 :(得分:8)
存在一些差异和权衡:
使用AtomicReference
get / set具有与volatile字段相同的 JMM语义(如javadoc状态),但AtomicReference
是一个包装器引用,因此对该字段的任何访问都涉及进一步的指针追踪。
内存占用量增加(假设压缩OOP环境,大多数虚拟机都是如此):
AtomicReference
= 4b + 16b(12b对象标题+ 4b ref field) AtomicReference
提供比挥发性引用更丰富的API。您可以使用AtomicFieldUpdater
或Java 9 VarHandle
重新获得易失性参考的API。如果您喜欢用剪刀跑步,也可以直达sun.misc.Unsafe
。 AtomicReference
本身是使用Unsafe
实现的。
所以,什么时候选择一个比另一个好:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
之间进行选择,在这种情况下,您倾向于支付可读性并为您的性能提升带来风险。如果这不是敏感区域,请选择AtomicReference
。库编写者通常使用这些方法的混合,具体取决于目标JDK,预期的API限制,内存约束等。答案 3 :(得分:6)
JDK source code是回答此类混淆的最好方法之一。如果查看AtomicReference中的代码,它会使用volatie变量进行对象存储。
private volatile V value;
所以,显然如果你要在AtomicReference上使用get()和set()就像使用volatile变量一样。但正如其他读者所评论的那样,AtomicReference提供了额外的CAS语义。因此,首先要确定是否需要CAS语义,如果只是,那么请使用AtomicReference。
答案 4 :(得分:2)
AtomicReference提供了一个普通的volatile变量无法提供的附加功能。当您阅读API时,您会知道这一点,但它也提供了一个对某些操作有用的锁。
但是,除非您需要此附加功能,否则我建议您使用普通的易失性字段。
答案 5 :(得分:0)
有时,即使您仅使用获取和设置,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;
但是,第二个更可能在将来的“代码清除”期间被某人意外删除。