深入理解CAS算法原理

Apr 16, 2021·
Bang
Bang
· 1 min read

CAS是Compare And Swap(比较和替换)的简称,是一种无锁算法,它是一种乐观锁技术。能够在不使用锁的情况下,保证对数据操作的原子性,解决多线程之间变量同步的问题。因为不涉及锁操作,所以具有交高的性能。

CAS 是如何保证原则性操作的

CAS中有3个参数

V:表示要更新的变量,主内存中的值
E:表示预期的值,线程中的值
N:表示新值

执行逻辑

CAS中有3个参数

  1. 先查询V,赋值给E。
  2. 然后比较V是否等于E。(数据很可能被其它线程修改,这里比较预期的值原始值是要确认数据没有被修改过)
  3. 如果不等,从新行执1,否则,将N赋值给V。
sequenceDiagram participant Thread1 participant Thread2 participant Memory Thread1->>Memory: 查询V (获取值为E1) Thread2->>Memory: 查询V (获取值为E1) Thread1->>Memory: 比较V是否等于E1 (是) Thread1->>Memory: 将N1赋值给V Thread2->>Memory: 比较V是否等于E1 (否) Thread2->>Memory: 重新查询V (获取值为E2) Thread2->>Memory: 比较V是否等于E2 (是) Thread2->>Memory: 将N2赋值给V Note right of=Memory: 最终V的值为N2

在绝大部分CPU平台都包含CAS指令,它在CPU中属于原子操作,如果当前CPU平台不支持CAS指令的时候,可以采共享内存(所有线程都监控主内存变化,比如Java的valatile)的方式实现。

CAS的优点

原子性

CAS 操作是原子的,不可被中断,确保了操作的完整性。

无锁

CAS 是一种无锁的同步机制,相比于传统的锁机制,它不需要阻塞线程,而是采用自旋的方式,通过不断尝试来完成操作,减少了线程争夺锁的开销。

乐观并发控制

CAS 是一种乐观并发控制的方式,它假设多线程操作不会频繁冲突,在真正发生冲突时会进行重试。

CAS的不足之处

ABA问题

因为CAS需要在操作的时候会检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

解决方案:使用版本号,在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A,这样就能确认当前的A其实是修改后的值了。

循环时间长开销大

CAS操作通常需要使用循环来重试,直到成功。如果长时间不成功,就会导致循环时间长,增加开销。

解决方案:可以通过使用backoff策略(逐渐增加重试间隔)来减少循环次数,降低开销。

只能保证一个共享变量的原子操作

当对一个共享变量执行操作时,可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性。

解决方案:使用锁来操作多个变量或者将多个变量合并成一个(比如1个long可以存储2个int)

平台相关性

CAS操作的实现通常依赖于硬件的支持,不同的硬件平台可能有不同的实现,这可能导致平台相关性。

解决方案:使用平台无关的抽象层,如Java中的java.utilconcurrent.atomic包。

Bang
Authors
Professor of Artificial Intelligence
My research interests include distributed robotics, mobile computing and programmable matter.