Loading...
墨滴

雷雷

2021/07/20  阅读:46  主题:默认主题

Java中的CAS,中高级必备知识。

满满干货,请耐心看完! CAS,compare and swap的缩写,中文翻译成比较并交换。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期 原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况, 它都会在 CAS 指令之前返回该 位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不 提取当前 值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位 置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

系统底层进行CAS操作的时候,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一 个线程会对总线加锁成功,加锁成功之后会执行cas操作,也就是说CAS的原子性实际上是CPU实 现的, 其实在这一点上还是有排他锁的,只是比起用synchronized, 这里的排他时间要短的 多, 所以在多线程情况下性能会比较好。


先看和synchronized、ReentrantLock区别。

synchronized 、 ReentrantLock 这种独占锁属于悲观锁,它是在假设需要操作的代码一定会发生冲 突的,执行代码的时候先对代码加锁,让其他线程在外面等候排队获取锁。悲观锁如果锁的时间比较 长,会导致其他线程一直处于等待状态,像我们部署的web应用,一般部署在tomcat中,内部通过线程 池来处理用户的请求,如果很多请求都处于等待获取锁的状态,可能会耗尽tomcat线程池,从而导致系 统无法处理后面的请求,导致服务器处于不可用状态。 除此之外,还有乐观锁,乐观锁的含义就是假设系统没有发生并发冲突,先按无锁方式执行业务,到最 后了检查执行业务期间是否有并发导致数据被修改了,如果有并发导致数据被修改了 ,就快速返回失 败,这样的操作使系统并发性能更高一些。cas中就使用了这样的操作。


举个例子:

如果你们的网站中有调用支付宝充值接口的,支付宝那边充值成功了会回调商户系统,商户系统接收到 请求之后怎么处理呢?假设用户通过支付宝在商户系统中充值100,支付宝那边会从用户账户中扣除 100,商户系统接收到支付宝请求之后应该在商户系统中给用户账户增加100,并且把订单状态置为成 功。

处理过程如下:

开启事务

获取订单信息

if(订单状态==待处理)

{ 给用户账户增加100 将订单状态更新为成功 }

返回订单处理成功

提交事务


这样的问题:

由于网络等各种问题,可能支付宝回调商户系统的时候,回调超时了,支付宝又发起了一笔回调请求, 刚好这2笔请求同时到达上面代码,最终结果是给用户账户增加了200,这样事情就搞大了,公司蒙受损 失,严重点可能让公司就此倒闭了。


解决办法:

那我们可以用乐观锁来实现,给订单表加个版本号version,要求每次更新订单数据,将版本号+1,那 么上面的过程可以改为:

获取订单信息,将version的值赋值给V_A

if(订单状态==待处理)

{

开启事务 给用户账户增加100

update影响行数 = update 订单表 set version = version + 1 where id = 订单号 and version = V_A;

if(update影响行数==1)

{ 提交事务 }

else{回滚事务 }

}

返回订单处理成功

上面的update语句相当于我们说的CAS操作,执行这个update语句的时候,多线程情况下,数据库会 对当前订单记录加锁,保证只有一条执行成功,执行成功的,影响行数为1,执行失败的影响行数为0, 根据影响行数来决定提交还是回滚事务。上面操作还有一点是将事务范围缩小了,也提升了系统并发处 理的性能。这个知识点希望你们能get到。

雷雷

2021/07/20  阅读:46  主题:默认主题

作者介绍

雷雷