原子操作的实现原理与CAS实现原子操作
原子操作表示操作是一个整体,不可再分。在并发场景下,如果无法保证某些操作是原子的,数据就无法一致,导致系统不可用。
处理器保证原子操作
处理器层面有两种方式实现原子操作:
通过总线锁来保证原子性。总线锁就是使用处理器提供的一个 LOCK # 信号,当一个处理器在总线上输出此信号时,其他处理器的请求就会被阻塞住,该处理器可以独占贡献内存。但此种方式,开销比较大,在锁总线期间,其他处理器无法操作其他内存地址的数据。
通过缓存锁定来保证原子性。使用“缓存锁定”的方式来实现复杂原子性。“缓存锁定”是指内存区域如果被缓存在处理器的缓存行中,并且在 Lock 操作期间被锁定,那么当它执行锁操作返回到内存时,处理器修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效。
CAS思想
Java 一般使用锁和循环 CAS 方式来实现原子操作。
CAS全称为 Compare-And-Swap 即比较并交换,使用方式是传入预期参数和替换参数,当预期参数与当前的参数数值一致时,会用替换参数来修改当前参数。先比较后交换的思想。
CAS 底层采用了处理器提供的 CMPXCHG 指令,这一指令就会对内存区域加锁,使其他处理器无法同时访问它。
CAS原子操作的三大问题
- ABA问题。如果一个值原来是 A,后来修改为 B,之后又变为 A,那么使用 CAS 进行检查时会认为数值没有发生变化,导致出错。
ABA 问题的解决思路是使用版本号,即 1A -> 2B -> 3A,在此期间版本发生变化,就不能进行替换操作。- 循环时间长开销大。
- 只能保证一个共享变量的原子操作。可以把多个共享变量封装为一个共享对象来操作。