English 中文(简体)
Java volatile reference 和 AtomicReference的区别
原标题:
  • 时间:2008-11-11 15:04:26
  •  标签:

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

最佳回答

简短的回答是:不。

java.util.concurrent.atomic包的文档中引用:

原子操作的访问和更新的记忆效应通常遵循易失性变量的规则:

  • get has the memory effects of reading a volatile variable.
  • set has the memory effects of writing (assigning) a volatile variable.

顺便说一下,那份文档非常好,一切都被解释得很清楚。


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

问题回答

不,没有。

AtomicReference提供额外的能力,即compareAndSet()方法和相关方法。如果你不需要这些方法,那么volatile引用提供的语义与AtomicReference.set()和.get()相同。

有一些不同之处和权衡:

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

  2. 内存印记被乘以(假设是压缩的OOPs环境,这对大多数虚拟机来说是正确的):

    • volatile ref = 4b
    • AtomicReference = 4b + 16b (12b object header + 4b ref field)
  3. AtomicReference提供了比volatile引用更丰富的API。您可以使用AtomicFieldUpdater或Java 9中的VarHandle来恢复volatile引用的API。如果您喜欢冒险,也可以直接使用sun.misc.UnsafeAtomicReference本身就是使用Unsafe实现的。

那么,什么情况下选择其中一个更好呢?

  • Only need get/set? Stick with a volatile field, simplest solution and lowest overhead.
  • Need the extra functionality? If this is a performance(speed/memory overhead) sensitive part of your code make a choice between AtomicReference/AtomicFieldUpdater/Unsafe where you tend to pay in readability and risk for your performance gain. If this not a sensitive area just go for AtomicReference. Library writers typically use a mix of these methods depending on targeted JDKs, expected API restrictions, memory constraints and so on.

JDK源代码是解决这种困惑的最佳途径之一。如果您查看AtomicReference中的代码,它使用了一个volatile变量来存储对象。

private volatile V value;

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

AtomicReference提供了比单纯的volatile变量更多的功能。当你阅读过API Javadoc后,你将会了解到这一点,但它也提供了一个锁,对某些操作非常有用。

但是,除非您需要此额外功能,否则建议使用普通的volatile字段。

有时即使您只使用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 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();

带着: (dài zhe)

Status statusCopy = status;

然而我觉得第二个更有可能在将来的“代码清理”中被某人意外删除。





相关问题
热门标签