Answers:
简短的答案是:不。
从java.util.concurrent.atomic
包装文件中。报价:
原子访问和更新的记忆效应通常遵循挥发物的规则:
get
具有读取volatile
变量的记忆效果。set
具有写入(分配)volatile
变量的记忆效果。
顺便说一句,该文档非常好,所有内容都得到了解释。
AtomicReference::lazySet
是一种较新的(Java 6+)操作,具有无法通过volatile
变量实现的语义。有关更多信息,请参见此帖子。
存在一些差异和折衷:
使用AtomicReference
get / set与volatile字段具有相同的JMM语义(如javadoc所述),但是则AtomicReference
是对引用的包装,因此对字段的任何访问都涉及进一步的指针追逐。
的内存占用乘以(假设压缩糟糕的环境,这是真实的大多数虚拟机):
AtomicReference
= 4b + 16b(12b对象标头+ 4b引用字段)AtomicReference
提供了比可变参考更丰富的API。您可以使用AtomicFieldUpdater
和或使用Java 9 a 重新获得易失性引用的API VarHandle
。sun.misc.Unsafe
如果您喜欢用剪刀跑步,也可以伸直。AtomicReference
本身是使用实现的Unsafe
。
因此,何时选择一个优于另一个:
AtomicReference
/ AtomicFieldUpdater
/ 之间进行选择,在此Unsafe
位置您倾向于付出可读性和性能风险。如果这不是敏感区域,那就去AtomicReference
。库编写者通常根据目标JDK,预期的API限制,内存限制等使用这些方法的混合。JDK源代码是解决此类混淆的最佳方法之一。如果您查看AtomicReference中的代码,它将使用volatie变量进行对象存储。
private volatile V value;
因此,显然,如果您只打算在AtomicReference上使用get()和set(),则就像在使用volatile变量一样。但是正如其他读者所评论的那样,AtomicReference提供了其他CAS语义。因此,首先决定是否要使用CAS语义,如果只需要,则使用AtomicReference。
AtomicReference
提供普通volatile变量不提供的其他功能。当您阅读API Javadoc时,就会知道这一点,但是它也提供了一个锁,对某些操作可能有用。
但是,除非您需要此附加功能,否则建议您使用纯volatile
字段。
volatile
字段可以像任何常规字段一样使用,而在访问必需的值和方法时AtomicReference
要使用它。get
set
有时,即使您仅使用获取和设置,AtomicReference可能也是一个不错的选择:
volatile示例:
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;
但是,第二个更有可能在将来的“代码清除”期间被某人意外删除。