Java volatile引用与原子参考

aoyhnmkz  于 2023-05-27  发布在  Java
关注(0)|答案(6)|浏览(97)

如果我只使用AtomicReference中的get()set()-方法,那么volatile对象引用和AtomicReference之间有什么区别吗?

vhipe2zx

vhipe2zx1#

简短的回答是:没有
java.util.concurrent.atomic软件包文档中。引用如下:
原子的访问和更新的存储器效应通常遵循易失性的规则:

  • get具有阅读volatile变量的内存效应。
  • set具有写入(赋值)volatile变量的内存效应。

顺便说一句,文档非常好,一切都得到了解释。
AtomicReference::lazySet是一个较新的(Java 6+)操作,它的语义无法通过volatile变量实现。请参阅this post)了解更多信息。

n53p2ov0

n53p2ov02#

不,没有。
AtomicReference提供的额外功能是compareAndSet()方法及其友元。如果不需要这些方法,volatile引用提供与AtomicReference.set()和.get()相同的语义。

n3schb8v

n3schb8v3#

有几个区别和权衡:
1.使用AtomicReference get/set与volatile字段具有相同的JMM语义(正如javadoc所述),但AtomicReference是引用的 Package 器,因此对字段的任何访问都涉及进一步的指针追踪
1.内存占用成倍增加(假设压缩OOP环境,这对大多数虚拟机都是如此):

  • volatile ref = 4b
  • AtomicReference = 4 b +16 b(12 b对象标头+4b参考字段)
  1. AtomicReference提供了比volatile引用更丰富的API。您可以使用AtomicFieldUpdater重新获得volatile引用的API,或者在Java 9中使用VarHandle。如果你喜欢用剪刀跑步,你也可以直接用sun.misc.UnsafeAtomicReference本身使用Unsafe实现。
    所以,什么时候选择一个比另一个好:
  • 只需要get/set?坚持使用易变的字段、最简单的解决方案和最低的开销。
  • 需要额外的功能?如果这是代码中对性能(速度/内存开销)敏感的部分,请在AtomicReference/AtomicFieldUpdater/Unsafe之间做出选择,您往往会为性能增益付出可读性和风险。如果这不是一个敏感的区域,就去AtomicReference。库编写者通常会根据目标JDK、预期API限制、内存限制等使用这些方法的混合。
wj8zmpe1

wj8zmpe14#

JDK source code是解决这类困惑的最佳方法之一.如果你看一下AtomicReference中的代码,它使用了一个volatie变量来存储对象。

private volatile V value;

所以,很明显,如果你只在AtomicReference上使用get()和set(),就像使用volatile变量一样。但正如其他读者评论的那样,AtomicReference提供了额外的CAS语义。因此,首先决定是否需要CAS语义,如果需要,则使用AtomicReference。

r7knjye2

r7knjye25#

AtomicReference提供了普通volatile变量不提供的附加功能。当你读过API Javadoc时,你会知道这一点,但它也提供了一个锁,这对某些操作很有用。
但是,除非您需要此附加功能,否则我建议您使用普通的volatile字段。

h6my8fg2

h6my8fg26#

有时候,即使你只使用gets和sets,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 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
  }
}

有人可能会说,你仍然可以有一个适当的副本,如果你取代:

Status status = statusWrapper.get();

其中:

Status statusCopy = status;

不过我猜第二个更有可能在将来的“代码清理”过程中被某人意外删除。

相关问题