146

volatile对象引用和AtomicReference我只使用get()and set()-methods from之间有什么区别AtomicReference吗?

4

6 回答 6

122

简短的回答是:不。

java.util.concurrent.atomic包文档。去引用:

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

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

顺便说一句,该文档非常好,并且对所有内容都进行了解释。


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

于 2008-11-11T15:17:05.580 回答
47

不,那里没有。

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

于 2008-11-11T15:20:33.983 回答
26

有几个区别和权衡:

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

  2. 内存占用成倍增加(假设一个压缩的OOP 环境,对于大多数 VM 来说都是如此):

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

那么,什么时候最好选择一个而不是另一个:

  • 只需要获取/设置?坚持使用易变字段、最简单的解决方案和最低的开销。
  • 需要额外的功能?AtomicReference如果这是代码的性能(速度/内存开销)敏感部分AtomicFieldUpdater,请Unsafe在您倾向于为可读性和性能增益风险付出代价之间做出选择。如果这不是一个敏感区域,那就去吧AtomicReference。库编写者通常根据目标 JDK、预期的 API 限制、内存限制等混合使用这些方法。
于 2017-07-19T10:10:38.117 回答
8

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

private volatile V value;

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

于 2013-02-03T15:24:41.673 回答
4

AtomicReference提供了普通 volatile 变量不提供的附加功能。当您阅读过 API Javadoc 时,您会知道这一点,但它还提供了一个锁,这对某些操作很有用。

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

于 2009-01-30T20:35:39.417 回答
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;

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

于 2020-06-05T09:40:41.187 回答