CAS循环高并发优势及单核心场景下的无阻塞安全性疑问
CAS循环:高并发优势与终止安全性的真相
问题背景
假设我们只有1个CPU核心,先看一段Java里的CAS循环示例:
private AtomicInteger count = new AtomicInteger(0); public void increment() { int current, next; do { current = count.get(); next = current + 1; } while (!count.compareAndSet(current, next)); }
从理论上看,确实存在线程卡进循环出不来的可能:比如某个线程刚算完next = current +1,就被操作系统切换走,其他线程趁机修改了count的值。等这个线程再回来执行CAS时肯定失败,要是每次都这么倒霉,就会一直循环下去。
核心疑问:
- CAS循环的安全性(不会让线程永久卡循环)是不是靠“CPU不会频繁切换线程”这个假设撑着?
- 如果是,怎么能知道CPU一般执行多少操作才会切换线程?
- 如果不是,那是什么保证CAS循环最终能结束?有没有操作系统层面的机制?
- 为啥CAS循环在高并发场景下更受欢迎?
一、为啥CAS是高并发的更优选择
CAS是无锁同步方式,对比传统的互斥锁(比如Java里的synchronized),优势很明显:
- 开销更低:不用在用户态和内核态之间来回切换,避免了锁带来的线程阻塞、唤醒等额外成本。
- 不阻塞线程:线程不会因为抢不到锁就被挂起,失败了可以立刻重试,反而减少了上下文切换的概率。
- 扩展性更好:高并发下,锁竞争会导致大量线程等着抢锁,而CAS的重试机制能更高效地利用CPU,尤其在多核机器上优势更突出。
二、CAS的安全性:真的依赖“CPU不频繁切换”吗?
答案是不依赖,它的终止保障来自操作系统的线程调度逻辑,而非“不切换”的假设。
1. 极端场景只是理论可能,现实中几乎不会发生
你说的“每次刚算完next就被切换”属于极端到离谱的情况,现代操作系统的线程调度器是讲“公平”的——它不会永远不让某个线程执行,每个就绪的线程都会分到CPU时间片。
2. 操作系统的调度机制在兜底
以常见的Linux CFS调度器为例:
- 每个线程都会拿到时间片(一般是1~10毫秒),在这个时间片里,线程能连续执行数千万条指令(3GHz的CPU,1毫秒就能跑300万条指令)。而CAS循环里的那几步操作(读current、算next、CAS)才几条指令,所以在一个时间片内,线程能重试N次CAS,根本不可能每次都卡在关键节点被切换。
- 上下文切换本身是有成本的(要保存寄存器、页表等状态,大概要几千到几万条指令),调度器不会没事就切换线程,只有在时间片用完、线程主动阻塞(比如等IO)、或者有更高优先级线程就绪时才会触发切换。
3. 怎么建立对CPU切换频率的直觉?
记住两个关键量级:
- 时间片是毫秒级:1~10毫秒,足够线程执行大量指令。
- 上下文切换是相对昂贵的操作:调度器不会频繁做,所以线程连续执行几十上百万条指令再被切换才是常态,不是刚执行两三条就被切走。
三、CAS循环为啥一定会终止?
CAS循环的终止是概率上的必然:
- 单核心场景下,同一时刻只有一个线程在跑,其他线程都在就绪队列等着。就算某个线程CAS失败,它会回到就绪队列,调度器迟早会再给它分配CPU时间。而其他线程修改完
count后,也会用完时间片被切换走,最终这个“倒霉”的线程总能赶上一次:读了current之后,没有其他线程来修改count,顺利完成CAS。 - 没有硬性的重试次数上限,但实际场景中,重试次数极少:低并发时可能一次就成,高并发时也不会超过几十次。
总结
CAS循环的安全性不靠“CPU不频繁切换”的假设,而是靠操作系统公平的线程调度和时间片机制。极端的永久循环在现实中几乎不可能出现,而它的无锁特性让它在高并发场景下比传统锁更高效、扩展性更好。
内容的提问来源于stack exchange,提问作者Turkhan Badalov
相关产品推荐
相关产品推荐

