Java易失性参考与AtomicReference

时间:2008-11-11 15:04:27

标签: java concurrency

如果我只使用volatileAtomicReference - 来自get()的方法,set()对象引用与AtomicReference之间是否有任何区别?

6 个答案:

答案 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)

存在一些差异和权衡:

  1. 使用AtomicReference get / set具有与volatile字段相同的 JMM语义(如javadoc状态),但AtomicReference是一个包装器引用,因此对该字段的任何访问都涉及进一步的指针追踪

  2. 内存占用量增加(假设压缩OOP环境,大多数虚拟机都是如此):

    • volatile ref = 4b
    • AtomicReference = 4b + 16b(12b对象标题+ 4b ref field)
  3. AtomicReference提供比挥发性引用更丰富的API。您可以使用AtomicFieldUpdater或Java 9 VarHandle重新获得易失性参考的API。如果您喜欢用剪刀跑步,也可以直达sun.misc.UnsafeAtomicReference本身是使用Unsafe实现的。

  4. 所以,什么时候选择一个比另一个好:

    • 只需要获取/设置?坚持使用易变的领域,最简单的解决方案和最低的开销。
    • 需要额外的功能吗?如果这是性能(速度/内存开销)代码的敏感部分,请在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;

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