比较并交换 (CAS) 原理

比较并交换 (CAS) 原理

synchronized相当于是悲观锁,CAS相当于是乐观锁。

悲观锁:我现在要操作一个共享数据,我很悲观,我认为我操作的过程中,一定会被人给修改,会导致数据错误;我在操作这个数据之前,先给这个数据加了一把锁,synchronized,在我操作这个数据的期间,就只能是我来操作,其他任何人都操作不了。

**乐观锁:**我感觉在我操作这个数据的过程中,应该不会被人给修改。我先修改吗,然后修改完之后要设置这个变量的最新的值,此时我会对比一下,当前的这个值,是不是跟我在操作前看到的这个值是一样的,如果是一样的,那么说明可能就没有被人给修改过,如果没有被人修改过,那么我就可以来设置最新的值。

CAS,Compare and Swap,就是比较和交换。java并发包借助CAS实现了乐观锁的思想,synchronized是悲观锁思想。

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

通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。

类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法可以对该操作重新计算。清单 3 说明了 CAS 操作的行为(而不是性能特征),但是 CAS 的价值是它可以在硬件中实现,并且是极轻量级的(在大多数处理器中):

举个例子吧,比如int a = 3,这是内存中的当前值,然后你CAS(3, 5),第一个是旧的预期值,如果3和a是一样的,那么就将a修改为5。

CAS的缺点

1.cpu开销大

自旋锁的目的是为了占着CPU的资源不释放,等到获取到锁立即进行处理。在高并发的情况下,如果多个线程同时更新一个值不成功,就会一直自旋,增大cpu压力。

2.不能保证代码块的原子性

cas保证的是一个变量的原子性操作,如果要保证多个变量或者代码块的原子性,就要使用synchronized。

3.ABA问题(加版本号)

对于某一个数据,有一个线程将它操作后又恢复原状,而我们无法分辨。

ABA问题

在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下

线程1,期望值为A,欲更新的值为B
线程2,期望值为A,欲更新的值为B
线程1抢先获得CPU时间片,而线程2因为其他原因阻塞了,线程1取值与期望的A值比较,发现相等然后将值更新为B,然后这个时候出现了线程3,期望值为B,欲更新的值为A,线程3取值与期望的值B比较,发现相等则将值更新为A,此时线程2从阻塞中恢复,并且获得了CPU时间片,这时候线程2取值与期望的值A比较,发现相等则将值更新为B,虽然线程2也完成了操作,但是线程2并不知道值已经经过了A->B->A的变化过程。

解决方法
在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值