CAS
Posted juzhuxiaozhu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS相关的知识,希望对你有一定的参考价值。
CAS
一. 简介
利用锁保证线程安全会造成CPU资源浪费(例如线程调度,线程上下文切换等),考虑到锁带来的开销,引入了无锁算法CAS(Compare And Swpa,比较交换),成为整个JUC体系最核心、最基础理论。
CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。
- 官方解释:我认为V的值应该是A,如果是,那么将V的值更新为B,否则不修改并告诉V的实际值是多少。其中:V代表内存值,A代表旧的预期值,B代表新的预期值。
- CAS的目的在于判断欲修改的值是否被修改过。CAS的过程一旦被打断,就会重新开始。以确保操作的原子性。
- 因为CAS涉及到线程的重新调度问题,所以需要结合具体的cpu内核架构来涉及,因此Java的CAS底层是依靠C语言实现的。目前市面上,服务器流行的内核架构都是支持CAS。
二. ABA问题
-
问题:CAS需要在操作值的时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,后来变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
-
解决思路:使用版本号。在变量前面追加版本号,每次变量更新的时候把版本号+1,那么A->B->A 就会变成1A->2B->3A。从JDK1.5 开始,JDK的Atomic包里提供了一个类Atomic包里提供了一个AtomicStampedReference来解决ABA问题。
public boolean compareAndSet( V expectedReference, // 预期引用 V newReference, // 更新后的引用 int expectedStamp, // 预期标志 int newStamp // 更新后的标志 )
compareAndSet
方法的作用首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
三. 缺陷
-
循环时间长,开销大
CAS不成功则会一直自旋,如果自旋
CAS
长时间不成功,会给CPU
带来非常大的执行开销。 -
只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环
CAS
的方式来保证原子操作,但是对多个共享变量操作时,循环CA
S就无法保证操作的原子性,这个时候就可以用锁。还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量
i=2,j=a
,合并一下ij=2a,然后用CAS
来操作ij。从Java 1.5开始,JDK提供了AtomicReference
类来保证引用对之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。
以上是关于CAS的主要内容,如果未能解决你的问题,请参考以下文章