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

CAS技术背景

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

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

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

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

它在java中依赖Unsafe类来实现,常见的CAS实现有AtomicInteger、AtomicLong、AtomicReference等,这些都是使用乐观锁的形式来实现多线程线程编程。

用法可见:Java - CAS用法及说明 - 简书


悲观锁,乐观锁

  • 悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会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的时候只有一个最终会成功,而其他的都会失败;但是失败的线程并不会被挂起,仅仅是尝试失败,并且允许再次尝试(当然也可以主动放弃);

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 一样】 并且  【期望值跟变量值一样】,则修改;否则不修改;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值