CAS(Compare and Swap)
Java 平台中的锁包括内部锁(排它锁)( Intrinsic Lock )和显式锁 ( Explicit Lock )。内部锁是通过synchronized关键字实现的;显式锁是通过java.concurrent.locks.Lock接口的实现类(如 java.concurrent.locks.ReentrantLock 类 ) 实现的。
synchronized(ReentrantLock)这种锁属于悲观锁
,即它始终都觉得会发生并发冲突,所以会屏蔽一切可能打破数据完整性的操作.
同时,还有一种锁叫乐观锁,即它觉得不会发生并发冲突,因此只在提交操作时检查是否违反数据完整性这些规则. 如果提交失败,它就会进行重试,乐观锁最常见的就是这里要说的CAS了.
CAS是一种高效实现线程安全性的方法
CAS机制主要是发生于Java中原子操作类(JUC)的底层实现中,它就是一种底层的实现思想,我们不会去直接使用它,我们使用的都是它包装好的类.
- 它支持原子更新操作,适用于
计数器
,序列发生器(即给变量自增的工具)
等场景. - 它属于乐观锁,号称lock-free
- CAS操作失败时由开发者决定是否尝试,还是执行别的操作,由此执行失败的线程不会被阻塞挂起,只会告知你失败了.
- CAS一定要与volatile变量配合使用,这样才能保证线程每次拿到的变量都是主内存中最新的那个值
- atomic包下的原子类就是使用的CAS机制
CAS思想
- CAS的操作包含三个操作数:
内存位置(C)
,预期原值(A)
和新值(B)
. - 执行CAS操作的时候,将
内存位置的值(即主内存的值)与预期原值
进行比较,如果相匹配,那么处理器就会将该内存位置的值更新为新值,否则处理器不做任何操作. - 例如:当一个线程想要修改共享变量的值,那么它首先在主内存中取出该值的副本或引用赋值给A,然后用A去参加运算,得到新值B,执行完毕需要更新共享变量的值的时候,就调用CAS方法区更新变量的值.
CAS多数情况下对开发者是透明的
- J.U.C的atomic包提供了常见的原子性数据类型以及引用,数组等相关原子类型的更新操作工具,是很多线程安全程序的首选.
- Unsafe类虽然提供了CAS服务,但因能操纵任意内存地址读写而由隐患.
- java9以后,可以使用Variable Handle API来替代Unsafe.
CAS缺点
- 若循环时间长,开销会很大.
- 只能保证一个共享变量的原子操作.
- ABA问题
即如果内存地址C初次读取的值为A,并且在准备赋值的比较的时候,它的值仍然为A,
此时我们就可以说它的值没有被其他线程改变过了吗?
他有可能被改变成了B然后又改变回了A. 但是CAS却会认为他没有被改变过.
解决方法: 它提供了一个带有标记的原子引用类AtomicStampedReference
,它可以通过控制变量值的版本来保证CAS的正确性.