并发编程(七)CAS原理解析

一、引言

  前面的并发编程学习多次提到的CAS这个原理,也参考了不少CAS的文章,想想还是自己写一篇总结好好理解一下CAS原理吧,作为并发包的基石,CAS原理在提升性能方面是有很大的用处的,很有必要去专门下功夫了解一下。

二、CAS的相关概念

基础概念

  CAS:Compare and Swap,即比较再交换。

  主要步骤:冲突检测数据更新

  用处:可以实现乐观锁

使用CAS之前存在的问题

  java在1.5之前都是靠synchronized关键字保证同步,synchronized是悲观锁,保证了无论哪个线程持有共享变量的锁,都会采用独占的方式来访问这些变量。这种情况下:

  • 1、在多线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延时,引起性能问题
  • 2、如果一个线程持有锁,其他的线程就都会挂起,等待持有锁的线程释放锁。
  • 3、如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能风险。

  简单的来说就是会存在很多性能问题

CAS原理分析

  核心操作:冲突检测数据更新

  特点:当多个线程尝试使用CAS同时更新同一个变量时,只有一个线程可以更新变量的值,其他的线程都会失败,失败的线程并不会挂起,而是告知这次竞争中失败了,并可以再次尝试

  三个操作数:

  • 需要读写的内存位置(V)
  • 预期原值(A)
  • 新值(B)

  一句话理解:我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

  PS:乐观锁是一种思想CAS只是这种思想的一种实现方式。、

  伪代码:

//伪代码,解析CAS过程
do{
    //1、备份旧数据(V)
    //2、把旧值设置为新值(B)
    //3、返回结果【成功返回新值(B),失败则返回旧的值(V)】
}while(/*比较旧值(V)和预期值(A)是否相等*/);

  图解:

  场景:t1,t2线程同时更新同一变量100的值

  解析:

  • 1、因为t1和t2线程都同时去访问同一变量100,所以他们会把主内存的值完全拷贝一份到自己的工作内存空间,所以t1和t2线程的预期值都为100。
  • 2、假设t1在与t2线程竞争中线程t1能去更新变量的值,而其他线程都失败。PS:失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次发起尝试
  • 3、t1线程去更新变量值改为101,然后写到内存中。此时对于t2来说,内存值变为了101,与预期值100不一致,就操作失败了【想改的值不再是原来的值】
  • 4、CAS 操作是基于共享数据不会被修改的假设,采用了类似于数据库的 commit-retry 的模式,当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。

三、CAS的应用

PS:Unsafe为我们提供了硬件级别的原子操作

四、ABA问题

问题的产生

  首先我们看一下ABA问题是怎么产生的,网上有一个很好的例子,这里用来借鉴一下,感兴趣的同学可以点击参考文章中的【漫画:什么是CAS机制】来详细了解,作者讲解得非常好,强烈推荐关注下原文作者“程序员小灰”:

 

 

 

 

解决方法

  解决方法:借助AtomicStampedReference类,加入版本号进行比较。

  原理:

  • 需要读写的内存位置(V)
  • 预期原值(A)
  • 新值(B)
  • 版本号(version)

  在原来的基础上加入版本号,读取公共变量的时候把当前版本号读取出来,比较的时候不仅比较内存位置值和预期原值,还要比较版本号,三者都相同时才进行替换。

  PS:详解AtomicStampedReference戳这里~

 

 

 

参考文章:

 

posted @ 2020-10-26 10:35  有梦想的肥宅  阅读(260)  评论(0编辑  收藏  举报