文章目录

  • 什么是CAS
  • CAS 举例说明
  • CAS 底层实现
  • CAS缺陷

什么是CAS

CAS 的全称是 Compare And Swap 即比较交换,其算法核心思想如下函数:CAS(V,E,N) 参数:

  1. V 表示要更新的变量
  2. E 预期值
  3. N 新值

如果 V 值等于 E 值,则将 V 的值设为 N。若 V 值和 E 值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。

通俗的理解就是 CAS 操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没线程修改该值,当前线程可以进行修改,也就是执行 CAS 操作,但如果期望值与当前线程不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作。

我们看一下例子:

  1. 在内存地址V当中,存储这值为10的变量


2. 此时线程1想要把变量的值增加1。对于线程1来说,旧的预期值为E=10,要修改的值 N=11。

3. 线程1要提交更新之前,另一个线程2抢先一步,把内存地址V的变量值先更改成了11


4. 线程1开始提交更新,首先进行E和内存V中实际值比较,发现E不等于V的实际值,提交失败。


5. 线程1重新获取内存地址V的当前值,并重新计算想要修改的新值,此时对线程1来说,E=11,N=12。这个重新尝试的过程被称为自旋。


6. 这一次没有发现其他线程改变地址V的值。线程1进行Compare ,发现N和地址V的实际值是相等的。


7.线程1进行swap,把地址V的值替换为N ,也就是12.

CAS 举例说明


public static int count = 0;public static void main(String[] args) {//for (int i=0;i<5;i++){new Thread(new Runnable() {@Overridepublic void run() {try{Thread.sleep(1000);}catch (Exception e){}//每个线程当中让count值自增100次for(int j=0;j< 10000;j++){count++;}}}).start();}try{Thread.sleep(2000);}catch (Exception e){}System.out.println("count=="+count);
}

如上示例程序,因为上面代码不是线程安全的,所以最终的结果可能会小于 50000。

那么怎么解决呢?可以加锁(synchronized)。

 public static int count = 0;public static void main(String[] args) {//for (int i=0;i<5;i++){new Thread(new Runnable() {@Overridepublic void run() {try{Thread.sleep(1000);}catch (Exception e){}//每个线程当中让count值自增100次for(int j=0;j< 10000;j++){synchronized (Demo1.class) {count++;}}}}).start();}try{Thread.sleep(2000);}catch (Exception e){}System.out.println("count=="+count);
}

加了同步锁以后,count自增的操作变成了原子性操作,所以最终的输出一定是count = 50000。

Synchronized 的确保证了线程安全,但是在某些情况下,却不是一个最优选择。

为什么这么说?关键在于性能问题

Synchronized 关键字会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLED状态,这个过程中设计到的操作系统用户模式和内核模式,代价比较高。

尽管jdk1.6 为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低

java原子操作类,指的是java.util.concurrent.atomic包下,一些列以Atomic开头的包装类。例如AtomicBoolean、AtomicInteger、AtomicLong。他们分别用于boolean、Integer、Long类型的原子性操作。

现在我们尝试在代码中引入**AtomicInteger **类:

 public static AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) {//for (int i=0;i<5;i++){new Thread(new Runnable() {@Overridepublic void run() {try{Thread.sleep(1000);}catch (Exception e){}//每个线程当中让count值自增100次for(int j=0;j< 10000;j++){synchronized (Demo1.class) {count.incrementAndGet();}}}}).start();}try{Thread.sleep(2000);}catch (Exception e){}System.out.println("count=="+count.get());
}

使用AtomicInteger之后,最终的输出结果同样可以保证是50000。并且在某些情况下,代码的性能会比Synchronized更好。

Atomic 操作类的底层,正是利用了CAS机制;

CAS 底层实现

下面看一下AtomicInteger的源代码

public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}
}private volatile int value;
public final int get() {return value;
}

这段代码是一个无限循环,也就是CAS的自旋。循环体当中做了三件事:

  1. 获取当前值
  2. 当前值+1,计算出目标值
  3. 进行CAS操作,如果成功则跳出循环,如果失败则重复上述步骤

这里需要注意的重点是get方法,这个方法的作用是获取变量的当前值。

如何保证获得的当前值时内存中的最新值呢?很简单,用volatile关键字来保证,

可是compareAndSet方法是如何保证原子性操作的呢??

接下来看一看compareAndSet方法的实现,以及方法所依赖对象的来历:

compareAndSet方法的实现很简单,只有一行代码。这里涉及到两个重要的对象,一个是unsafe,一个是valueOffset

**什么是unsafe呢?**Java语言不像C,C++那样可以直接访问底层操作系统,但是JVM为我们提供了一个后门,这个后门就是unsafe。unsafe为我们提供了硬件级别的原子操作。

至于valueOffset对象,是通过unsafe.objectFieldOffset方法得到,所代表的是AtomicInteger对象value成员变量在内存中的偏移量。我们可以简单地把valueOffset理解为value变量的内存地址。

而unsafe的compareAndSwapInt方法参数包括了这三个基本元素:valueOffset参数代表了V,expect参数代表了A,update参数代表了B。

正是unsafe的compareAndSwapInt方法保证了Compare和Swap操作之间的原子性操作。

CAS缺陷

1.ABA 问题

因为CAS需要在操作值的时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A。举个通俗点的例子,你倒了一杯水放桌子上,干了点别的事,然后同事把你水喝了又给你重新倒了一杯水,你回来看水还在,拿起来就喝,如果你不管水中间被人喝过,只关心水还在,这就是ABA问题。
如果你是一个讲卫生讲文明的小伙子,不但关心水在不在,还要在你离开的时候水被人动过没有,因为你是程序员,所以就想起了放了张纸在旁边,写上初始值0,别人喝水前麻烦先做个累加才能喝水。

2.循环时间长 开销大

自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销

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

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子
性,这个时候就可以用锁。还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java 1.5开始,JDK提供了AtomicReference类来保证引用对之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作

什么是CAS?CAS有什么缺点?相关推荐

  1. 什么是CAS?CAS的作用以及缺点

    老顾聊技术 2019-06-03 00:28:00 欢迎关注头条号:老顾聊技术 精品原创技术分享,知识的组装工 前言 这道题是考察面试者的并发编程的知识,关于悲观锁和乐观锁的. 锁 回答这个问题,可以 ...

  2. linux系统怎么安装cas,CAS 在Linux中安装与配置

    一.首先将 /root/cas/ 下的 page文件夹删除 只留以下两个 二.创建cas 文件夹  并将root 下的tomcat压缩包 和 cas.war 复制进去 [root@hostname ~ ...

  3. php 模拟 cas,CAS的PHP客户端实践:PHP程序实现单点登录

    loading... 兄弟近日尝试将一个php程序以单点登录方式和原有的系统整合在一起. 验证服务器选用的是CAS,其提供有相应的php客户端. 整个过程如下: 1.搭建CAS服务器 2.搭建php应 ...

  4. C语言链表无锁化CAS,CAS无锁操作

    主要讲的是<Implementing Lock-Free Queues>的论点,具体直接看论文最好.这里总结些要点. CAS就是Compare And Swap.gcc可以调用: __sy ...

  5. CAS——cas server环境搭建

    环境要求 JDK 8+ CAS 5.2 tomcat 8+ cas server 客户端模板下载 https://github.com/apereo/cas-overlay-template 修改ho ...

  6. Java多线程学习三十九:CAS 有什么缺点?

    CAS 有哪几个主要的缺点. 首先,CAS 最大的缺点就是 ABA 问题. 决定 CAS 是否进行 swap 的判断标准是"当前的值和预期的值是否一致",如果一致,就认为在此期间这 ...

  7. 从底层吃透java内存模型(JMM)、volatile、CAS

    前言 随着计算机的飞速发展,cpu从单核到四核,八核.在2020年中国网民数预计将达到11亿人.这些数据都意味着,作为一名java程序员,必须要掌握多线程开发,谈及多线程,绕不开的是对JMM(Java ...

  8. Java中的锁原理、锁优化、CAS、AQS详解

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:景小财 www.jianshu.com/p/e674ee68 ...

  9. 面试官:说说 Java 中的 Unsafe 和 CAS

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://urlify.cn/nAVjM ...

最新文章

  1. rsyslog的学习
  2. intent和手势探测
  3. 如何在Amazon AWS上设置一台Linux服务器
  4. VTK:PolyData之PointLocatorRadius
  5. sed之G、H、g、h使用
  6. 合肥注册公司(各区注册地点说明)
  7. ActiveMQ 认证
  8. Atitit 法学体系树与知识点attilax大总结 法学体系 0301法学类 030101 法学理论 宪法 行政法 民法 商法 婚姻法和继承法 经济法 社会法 刑法 民事诉讼法 行政诉讼法
  9. JavaScript库和框架
  10. 自己设计个动态屏保吧
  11. 用命令修改oracle的密码,用命令修改Oracle数据库密码
  12. 树的计数 + prufer序列与Cayley公式(转载)
  13. Oracle EBS流程之--PO ER Model
  14. 深入剖析RGB、CMYK、HSB、LAB
  15. python计算加权平均分_python – 使用pandas数据帧计算加权平均值
  16. 求学信计算机专业英语,求学信模板英文
  17. 电影光盘的vob格式视频如何转换成mp4格式
  18. MyExcel 3.9.8 版本发布
  19. leetcode(17~30)
  20. 范美忠妻子:美忠是个好男人

热门文章

  1. 轮毂电机主动减振系统及其垂向性能优化
  2. 鸿蒙系统安卓模拟器,华为鸿蒙OS再次爆出缺陷!被误判为安卓模拟器:不属于手机系统设备...
  3. 博客质量分计算(一)
  4. arcgis生成剖面图(利用3D Analyst 工具上的插入线工具 )
  5. 《C语言程序设计》课程设计 -- 火车票票务管理系统
  6. 等待半世的婚礼——林汐
  7. Visual C++6.0的安装和使用
  8. 【文献阅读】ShEF: Shielded Enclaves for Cloud FPGAs
  9. ubuntu串口计数
  10. css vertical-align属性详解