Java volatile参考与AtomicReference


135

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

Answers:


114

简短的答案是:不。

java.util.concurrent.atomic包装文件中。报价:

原子访问和更新的记忆效应通常遵循挥发物的规则:

  • get具有读取volatile变量的记忆效果。
  • set具有写入(分配)volatile变量的记忆效果。

顺便说一句,该文档非常好,所有内容都得到了解释。


AtomicReference::lazySet是一种较新的(Java 6+)操作,具有无法通过volatile变量实现的语义。有关更多信息,请参见此帖子


11
更长的答案是?
Julien Grenier

同意 我们至少需要一个链接。
朱利安·查斯顿


42

不,那里没有。

AtomicReference提供的附加功能是compareAndSet()方法和朋友。如果不需要这些方法,则可变引用提供与AtomicReference.set()和.get()相同的语义。


14

存在一些差异和折衷:

  1. 使用AtomicReferenceget / set与volatile字段具有相同的JMM语义(如javadoc所述),但是则AtomicReference是对引用的包装,因此对字段的任何访问都涉及进一步的指针追逐

  2. 内存占用乘以(假设压缩糟糕的环境,这是真实的大多数虚拟机):

    • 挥发性参考= 4b
    • AtomicReference = 4b + 16b(12b对象标头+ 4b引用字段)
  3. AtomicReference提供了比可变参考更丰富的API。您可以使用AtomicFieldUpdater和或使用Java 9 a 重新获得易失性引用的API VarHandlesun.misc.Unsafe如果您喜欢用剪刀跑步,也可以伸直。AtomicReference本身是使用实现的Unsafe

因此,何时选择一个优于另一个:

  • 只需要获取/设置?坚持使用易变的字段,最简单的解决方案和最低的开销。
  • 需要额外的功能吗?如果这是您的代码中对性能(速度/内存开销)敏感的部分,请在AtomicReference/ AtomicFieldUpdater/ 之间进行选择,在此Unsafe位置您倾向于付出可读性和性能风险。如果这不是敏感区域,那就去AtomicReference。库编写者通常根据目标JDK,预期的API限制,内存限制等使用这些方法的混合。

7

JDK源代码是解决此类混淆的最佳方法之一。如果您查看AtomicReference中的代码,它将使用volatie变量进行对象存储。

private volatile V value;

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


13
“ JDK源代码是解决此类困惑的最佳方法之一” =>我不一定同意-Javadoc(这是该类的约定)是最佳方法。您在代码中找到的内容回答了特定实现的问题,但是代码可以更改。
assylias

4
例如,hashmap中的此变量在JDK 6中是易失性的,而在Java 7中不再是易失性的。您是否基于变量是易失性的事实而编写了代码,所以在升级JDK时它会坏掉的。不同,但你明白了。
assylias 2013年

那是CAS的标准缩写吗?
阿巴斯(Abbas)

1
比较并交换=)
无止境的

4

AtomicReference提供普通volatile变量不提供的其他功能。当您阅读API Javadoc时,就会知道这一点,但是它也提供了一个锁,对某些操作可能有用。

但是,除非您需要此附加功能,否则建议您使用纯volatile字段。


因此,区别在于它们的性能。如果没有区别,您将永远不会建议使用一个。
英国电信

性能基本相同。AtomicRefrence增加了复杂性和内存使用量。
彼得·劳瑞

@BT volatile字段可以像任何常规字段一样使用,而在访问必需的值和方法时AtomicReference要使用它。getset
David Harkness 2014年

0

有时,即使您仅使用获取和设置,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;

但是,第二个更有可能在将来的“代码清除”期间被某人意外删除。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.