如何理解 AtomicInteger 里的 CAS 操作?

2017-04-02 17:15:12 +08:00
 esolve
为何要循环直到成功?如果这期间有另外的线程更改了 value ,导致 compareAndSet ()返回 false ,那这就表面已经不是原子性了吧,还继续重复有啥意义?


public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}

为何要循环直到成功?如果这期间有另外的线程更改了 value ,导致 compareAndSet ()返回 false ,那这就表面已经不是原子性了吧,还继续重复有啥意义?
1547 次点击
所在节点    问与答
6 条回复
kaneg
2017-04-02 18:07:23 +08:00
因为 cas 是原子的,所以返回 true 就证明原子操作成功,该循环退出。若 cas 操作失败,说明被其他线程抢先,则继续循环,直到 cas 成功。

用一句话总结就是:宏观的原子性操作是由无数的 cas 原子操作失败换来的唯一一次成功。
honeycomb
2017-04-02 18:18:07 +08:00
因为它的目的是要自增 1 ,且不需要保证调用它的线程的顺序:

一群线程在竞争这个原子变量时,对于线程 A ,它的一次操作对应于 current 为 1 时自增到 2 是可以的,如果被另一个线程抢先的话, A 等下一个循环,从 2 自增到 3 也是可以的。

从计数的角度来如此更换一下顺序不影响结果。

如果竞争非常激烈,这样的乐观锁的性能不一定会比悲观锁性能好。
esolve
2017-04-02 18:50:52 +08:00
@kaneg 被其他线程抢先了,那还干嘛继续循环?
譬如我现在 AtomicInteger 的值是 4,我期望其自赠变成 5
但是被其他线程抢先了
那我就应该放弃这次自赠啊
如果想继续,那就再调用一下 getAndIncrement 啊
esolve
2017-04-02 22:17:23 +08:00
dingdingding
slixurd
2017-04-02 22:38:58 +08:00
@esolve 因为你理解的作用不对....
原始值是 4,他能保证执行了 2 次 getAndIncrement 结果一定是 6 的...
wwqgtxx
2017-04-02 23:42:46 +08:00
你要执行的是把一个变量+1 这个操作,而不是保证一定是访问的瞬间值+1 这个操作,所以如果同时有 n 个线程调用 getAndIncrement ,最后的结果应该是原始值+n 而不是原始值+1

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/352174

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX