我知道volatile
允许知名度, AtomicInteger
允许原子。 所以,如果我使用挥发性AtomicInteger
,这是否意味着我没有使用任何更多的同步机制?
例如。
class A {
private volatile AtomicInteger count;
void someMethod(){
// do something
if(count.get() < 10) {
count.incrementAndGet();
}
}
这是线程安全的?
我相信, Atomic*
实际上给两个原子性和波动性。 所以,当你调用(比方说) AtomicInteger.get()
保证能让你获得最新的值。 这是记录在在java.util.concurrent.atomic
包文档 :
用于访问和原子能的更新记忆效应一般遵循规则挥发物,如Java™语言规范的第17.4陈述。
- GET具有读取volatile变量的内存效果。
- 组具有写入(分配)挥发性变量的记忆效应。
- lazySet具有写入(分配)volatile变量除了它允许使用本身不征收重排的约束与普通的非易失性写入后续的(而不是以前的)内存的动作重新排序的记忆效应。 在其他使用上下文,> - lazySet可能归零出来的时候,垃圾收集,是永远不会再次访问参考的缘故适用。
- weakCompareAndSet自动读取和写入有条件的变量,但不会产生任何的之前发生的排序,所以不提供任何担保相对于以前或以后的读取和比weakCompareAndSet的目标以外的任何变量写道。
- compareAndSet和如getAndIncrement所有其他的读取和更新操作同时具有读取和写入volatile变量的内存效果。
现在,如果你有
volatile AtomicInteger count;
该volatile
部分意味着每个线程将使用最新AtomicInteger
参考,而事实上,它是一个AtomicInteger
意味着,你还可以看到该对象的最新值。
这是不常见(IME)需要这一点-因为通常你不会重新分配count
是指不同的对象。 相反,你必须:
private final AtomicInteger count = new AtomicInteger();
在这一点上,事实证明这是一个final
变量意味着所有的线程将被处理同样的对象-而事实上,它是一个Atomic*
对象表示,他们将对该对象中看到最新的值。
我说没有,它不是线程安全的,如果你定义线程安全的,有在单线程模式和多线程模式相同的结果。 在单线程模式,计数将永远不会大于10,但在多线程模式下就可以了。
问题是, get
和incrementAndGet
是原子,但是一个if
不。 请记住,非原子操作可以在任何时候暂停。 例如:
-
count = 9
目前。 - 线程A运行
if(count.get() <10)
并得到true
和停在那里。 - B线程运行
if(count.get() <10)
并得到true
太所以它运行count.incrementAndGet()
和饰面。 现在, count = 10
。 - 线程A简历和运行
count.incrementAndGet()
现在count = 11
,这将永远不会在单线程模式发生。
如果你想使它线程安全的,而无需使用synchronized
是比较慢,试试这个,而不是执行:
class A{
final AtomicInteger count;
void someMethod(){
// do something
if(count.getAndIncrement() <10){
// safe now
} else count.getAndDecrement(); // rollback so this thread did nothing to count
}
答案是有这个代码
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java
这是的AtomicInteger的源代码。 该值是挥发性。 所以,里面的AtomicInteger使用挥发性。
为了保持原有的语义,并且支持多线程,你可以这样做:
public class A {
private AtomicInteger count = new AtomicInteger(0);
public void someMethod() {
int i = count.get();
while (i < 10 && !count.compareAndSet(i, i + 1)) {
i = count.get();
}
}
}
这避免了任何线程曾经看到数达到10。