如果我只使用AtomicReference
的get()
和set()
方法,volatile
对象引用和AtomicReference
之间有什么区别吗?
简短的回答是:不。
从java.util.concurrent.atomic
包的文档中引用:
原子操作的访问和更新的记忆效应通常遵循易失性变量的规则:
get
has the memory effects of reading avolatile
variable.set
has the memory effects of writing (assigning) avolatile
variable.
顺便说一下,那份文档非常好,一切都被解释得很清楚。
AtomicReference::lazySet
是一个较新的(Java 6+)操作,其语义无法通过volatile
变量实现。有关更多信息,请参见此帖子。
不,没有。
AtomicReference提供额外的能力,即compareAndSet()方法和相关方法。如果你不需要这些方法,那么volatile引用提供的语义与AtomicReference.set()和.get()相同。
有一些不同之处和权衡:
使用
AtomicReference
的get/set与volatile字段具有相同的JMM语义(如javadoc所述),但AtomicReference
是对引用的包装器,因此对字段的任何访问都涉及进一步的指针追踪。内存印记被乘以(假设是压缩的OOPs环境,这对大多数虚拟机来说是正确的):
- volatile ref = 4b
AtomicReference
= 4b + 16b (12b object header + 4b ref field)
AtomicReference
提供了比volatile引用更丰富的API。您可以使用AtomicFieldUpdater
或Java 9中的VarHandle
来恢复volatile引用的API。如果您喜欢冒险,也可以直接使用sun.misc.Unsafe
。AtomicReference
本身就是使用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 forAtomicReference
. 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;
然而我觉得第二个更有可能在将来的“代码清理”中被某人意外删除。
- winforms
- combobox
- fogbugz
- java
- date
- internationalization
- asp.net
- iis
- url-rewriting
- urlrewriter
- c#
- enums
- ocaml
- haxe
- algorithm
- string
- viewstate
- .net
- c++
- c
- symbol-table
- mysql
- database
- postgresql
- licensing
- migration
- vb.net
- vb6
- declaration
- vb6-migration
- python
- psycopg2
- backup
- vmware
- virtualization
- gnu-screen
- authentication
- desktop
- excel
- xll
- cultureinfo
- regioninfo
- oracle
- client
- session
- download
- html
- virtual
- constructor
- scenarios
- perl
- full-text-search
- javascript
- ajax
- testing
- oop
- inheritance
- vim
- encapsulation
- information-hiding