李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
CAS简介
Leefs
2020-02-22 PM
1809℃
0条
# CAS简介 ### 一、CAS概念 CAS的全称为Compare And Swap即比较并交换,它是一条CPU并发原语。 它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值。看起来这是两步操作,但是由于底层硬件的支持,使两步操作能一步完成,从而保证了原子性,避免了独占锁的资源浪费。 CAS并发原语体现在Java语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。 **CAS操作包含三个操作数--内存位置(V)、预期原值(A)和新值(B)。**如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。 **代码示例** ```java public class CASDemo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5); System.out.println(atomicInteger.compareAndSet(5,2019)+"\t current data:"+atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(5,2020)+"\t current data:"+atomicInteger.get()); } } ``` **运行结果** ```java true current data:2019 false current data:2019 ``` **分析** > compareAndSet(v1,v2) > > v1:预期值 > > v2:修改后的值 首先创建AtomicInteger对象,并为其附初始值5,当第一次调用AtomicInteger对象的compareAndSet方法时因为AtomicInteger初始值为5与预期值相同,所以返回true,将之前的初始值5改成2019。第二次调用AtomicInteger对象的compareAndSet方法因为预期值5与初始值2019不相同,所以返回false,对之前的初始值不做任何操作。 ### 二、CAS底层实现 CAS主要是通过Unsafe类来操作底层硬件来实现原子性。 ```java public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; //value是volatile修饰的,保证了线程之间的可见性 ``` **UnSafe类**是CAS的核心类,由于Java方法无法直接访问底层,需要通过本地(native)方法来访问,基于该类可以直接操作特定的内存数据。 UnSafe类在于sun.misc包中,其内部方法操作可以向C指针一样直接操作内存,因为Java中CAS操作依赖于UNSafe类的方法。注意UnSafe类中所有的方法都是native修饰的,也就是说UnSafe类中的方法都是直接调用操作底层资源执行响应的任务 > + 变量ValueOffset:它是该变量在内存中的偏移地址,因为UnSafe就是根据内存偏移地址获取数据的 > + 变量value:被volatile修饰,保证了多线程之间的可见性。 **Unsafe类中的getAndAddInt源码解读** ```java public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; } ``` > + var1:AtomicInteger对象本身 > + var2:该对象值的引用地址 > + var4:需要变动的数值 > + var5:是用var1、var2找出内存中的值 用该对象当前的值与var5比较 如果相同,更新var5的值并且返货true 如果不同,继续取值然后比较,直到更新完成 **执行过程分析** 假设线程A和线程B两个线程同时执行getAndAddInt操作(分别在不同的CPU上): 1. 1.AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,根据JMM模型,线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存. 2. 2.线程A通过getIntVolatile(var1,var2) 拿到value值3,这时线程A被挂起. 3. 3.线程B也通过getIntVolatile(var1,var2) 拿到value值3,此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存中的值也是3 成功修改内存的值为4 线程B执行完成 4. 4.这时线程A恢复,执行compareAndSwapInt方法比较,发现自己手里的数值和内存中的数字4不一致,说明该值已经被其他线程抢先一步修改了,那A线程修改失败,只能重新来一遍了 5. 5.程A重新获取value值,因为变量value是volatile修饰,所以其他线程对他的修改,线程A总是能够看到,线程A继续执行compareAndSwapInt方法进行比较替换,直到成功. ### 三、CAS缺点 1. 1.循环时间开销容易太大 因为CAS方法内部有do-while循环 ``` public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; } ``` 如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。 2. 2.只能保证一个变量的原子性 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是,对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。 ### 总结 CAS(Compare-And-Swap):比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,否则继续比较指导主内存和工作内存中的值一致为止。 CAS应用 CAS有三个操作数,内存值V,旧的预期值A,要修改的更新值B。 当且仅当预期值A和内存值V相同,将内存值V修改为B,否则什么都不做。
标签:
Java
,
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/637.html
上一篇
单例模式volatile分析
下一篇
CAS的ABA问题及解决
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
标签云
字符串
Spark
Hbase
Stream流
ClickHouse
JavaWEB项目搭建
Java
二叉树
锁
Kibana
VUE
排序
Zookeeper
Sentinel
Jquery
Elasticsearch
JavaSE
CentOS
DataWarehouse
散列
Java阻塞队列
Azkaban
Python
nginx
SpringCloudAlibaba
Tomcat
正则表达式
Typora
ajax
Yarn
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞