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 someone 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
}
}
6条答案
按热度按时间vhipe2zx1#
简短的回答是:没有
从
java.util.concurrent.atomic
软件包文档中。引用如下:原子的访问和更新的存储器效应通常遵循易失性的规则:
get
具有阅读volatile
变量的内存效应。set
具有写入(赋值)volatile
变量的内存效应。顺便说一句,文档非常好,一切都得到了解释。
AtomicReference::lazySet
是一个较新的(Java 6+)操作,它的语义无法通过volatile
变量实现。请参阅this post)了解更多信息。n53p2ov02#
不,没有。
AtomicReference提供的额外功能是compareAndSet()方法及其友元。如果不需要这些方法,volatile引用提供与AtomicReference.set()和.get()相同的语义。
n3schb8v3#
有几个区别和权衡:
1.使用
AtomicReference
get/set与volatile字段具有相同的JMM语义(正如javadoc所述),但AtomicReference
是引用的 Package 器,因此对字段的任何访问都涉及进一步的指针追踪。1.内存占用成倍增加(假设压缩OOP环境,这对大多数虚拟机都是如此):
AtomicReference
= 4 b +16 b(12 b对象标头+4b参考字段)AtomicReference
提供了比volatile引用更丰富的API。您可以使用AtomicFieldUpdater
重新获得volatile引用的API,或者在Java 9中使用VarHandle
。如果你喜欢用剪刀跑步,你也可以直接用sun.misc.Unsafe
。AtomicReference
本身使用Unsafe
实现。所以,什么时候选择一个比另一个好:
AtomicReference
/AtomicFieldUpdater
/Unsafe
之间做出选择,您往往会为性能增益付出可读性和风险。如果这不是一个敏感的区域,就去AtomicReference
。库编写者通常会根据目标JDK、预期API限制、内存限制等使用这些方法的混合。wj8zmpe14#
JDK source code是解决这类困惑的最佳方法之一.如果你看一下AtomicReference中的代码,它使用了一个volatie变量来存储对象。
所以,很明显,如果你只在AtomicReference上使用get()和set(),就像使用volatile变量一样。但正如其他读者评论的那样,AtomicReference提供了额外的CAS语义。因此,首先决定是否需要CAS语义,如果需要,则使用AtomicReference。
r7knjye25#
AtomicReference
提供了普通volatile变量不提供的附加功能。当你读过API Javadoc时,你会知道这一点,但它也提供了一个锁,这对某些操作很有用。但是,除非您需要此附加功能,否则我建议您使用普通的
volatile
字段。h6my8fg26#
有时候,即使你只使用gets和sets,AtomicReference也可能是一个不错的选择:
volatile示例:
使用AtomicReference,它将是:
有人可能会说,你仍然可以有一个适当的副本,如果你取代:
其中:
不过我猜第二个更有可能在将来的“代码清理”过程中被某人意外删除。