AtomicInteger 中的“比较和设置”如何工作
Posted
技术标签:
【中文标题】AtomicInteger 中的“比较和设置”如何工作【英文标题】:How does "Compare And Set" in AtomicInteger works 【发布时间】:2015-12-14 13:20:36 【问题描述】:AtomicInteger
使用两个概念:CAS 和volatile
变量。
使用volatile
变量可确保当前值对所有线程可见并且不会被缓存。
但我对 CAS(compare AND set) 的概念感到困惑,如下所述:
public final int getAndIncrement()
for (;;)
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
我的问题是whatif(compareAndSet(current, next)
返回false
?值不会更新吗?
在这种情况下,当线程执行以下情况时会发生什么:
private AtomicInteger count = new AtomicInteger();
count.incrementAndGet();
【问题讨论】:
blog.slaks.net/2013-07-22/thread-safe-data-structures 更完整的解释:baeldung.com/java-atomic-variables 循环一直存在,直到下一个值已成功更新,但由于 java 8+ 你可以简单地调用 getAndAdd(1),NB 只需要在这里传递 1 的增量。不再需要循环,一切都由 JDK 照顾。 docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/… 【参考方案1】:原子对象利用Compare and Swap 机制使它们成为原子对象 - 即可以保证值是指定的现在在新的价值。
您发布的代码不断尝试将当前值设置为比以前大一。请记住,另一个线程也可能执行了get
并且也在尝试设置它。如果两个线程相互竞争以更改值,则其中一个增量可能会失败。
考虑以下场景:
-
线程 1 调用
get
并获取值 1
。
线程 1 计算 next
为 2
。
线程 2 调用 get
并获取值 1
。
线程 2 计算出 next
为 2
。
两个线程都尝试写入值。
现在由于原子性 - 只有一个线程会成功,另一个将从compareAndSet
接收false
并再次运行。
如果不使用这种机制,那么两个线程很可能都会递增该值,从而导致实际上只进行一次递增。
只有在多个线程同时写入变量时,令人困惑的无限循环for(;;)
才会真正循环。在非常重的负载下,它可能会循环数次,但应该会很快完成。
【讨论】:
但是怎么做呢?两个线程可以同时Campare,然后同时Swap。应该有某种阻塞或等待【参考方案2】:for (;;)
是一个无限循环,所以它只会重试。
【讨论】:
以上是关于AtomicInteger 中的“比较和设置”如何工作的主要内容,如果未能解决你的问题,请参考以下文章
java中的原子操作类AtomicInteger及其实现原理
AtomicInteger类中getAndIncrement方法中的spin是啥意思?