欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(转载整理)CAS----无锁技术

程序员文章站 2022-03-08 17:54:34
...

CAS技术背景

关于同步,很多人都知道synchronized,Reentrantlock等加锁技术,这种方式也很好理解,是在线程访问的临界区资源上建立一个阻塞机制,需要线程等待;

其它线程释放了锁,它才能运行。这种方式很显然是奏效的,但是它却带来一个很大的问题:程序的运行效率。线程的上下文切换是非常耗费资源的,而等待又会有一定的时间消耗,

那么有没有一种方式既能控制程序的同步效果,又能避免这种锁带来的消耗呢?答案就是无锁技术。

cas的英文翻译全称是compare and set ,也就是【比较替换】技术;


悲观锁,乐观锁

  • 悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
  • 乐观锁,顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制;

注意:cas技术是一种乐观锁


CAS原理

cas技术包含三个参数,CAS(V,E,N);

  • V(variile)--------欲更新的变量;
  • E  (Excepted)  ----预期的值;
  • N  (New) -----------新值;

若V等于E值的时候吗,将V的值设为N;

若V值和E值不同,则说明已经有其它线程对该值做了更新,则当前线程什么都不做,直接返回V值。


举个例子,假如现在有一个变量int a=5,我想要把它更新为6(那么变量是5,期望值可能是5也可能不是5,新值是6)

有两种情况:

期望值是5----》cas(5,5,6)----》修改成功;

期望值不是5----》cas(5,7,6)----》没有修改成功;

关于期望值E,我感觉这个E值是通过数据库查询得到的(没具体验证);


当多个线程同时使用CAS的时候只有一个最终会成功,而其他的都会失败;但是失败的线程并不会被挂起,仅仅是尝试失败,并且允许再次尝试(当然也可以主动放弃);

(转载整理)CAS----无锁技术

ABA问题和解决方案

  • 问题描述:

火锅店在五一期间做活动,老用户凡是卡里余额小于20的,赠送10元,每人只可享受一次;最后发现赔钱赔哭了;

  • 有问题的实现方式:

写一个判断,开启10个线程,然后判断小于20的,一律加20;

  • 问题所在

有个线程A去判断账户里的钱此时是15,满足条件,直接+20,这时候卡里余额是35;但是此时不巧,正好在连锁店里,这个客人正在消费,又消费了20,此时卡里余额又为15,线程B去执行扫描账户的时候,发现它又小于20,又用过cas给它加了20,这样的话就相当于加了两次,这样循环往复肯定把老板的钱就坑没了!

  • 解决办法

问题所在:数据库字段目前值是2,有两种情况,数据库字段没被改过一直是2,也可能是原本是2,改为7,又改回为2;

若对修改的次数有限制,用一个字段判断就不行了,得再加一个字段(时间戳);

类似于:cas(V+stamp,E+stamp,N);

//参数代表的含义分别是 期望值,写入的新值,期望标记,新标记值
public boolean compareAndSet(V expected,V newReference,int expectedStamp,int newStamp);

若【expectedStamp 跟 newStamp 一样】 并且  【期望值跟变量值一样】,则修改;否则不修改;