乐观锁和悲观锁的简单理解

乐观锁和悲观锁的简单理解

一、悲观锁

每次读取数据时认为其他线程会修改这个数据,所以每次读的时候都会加锁,实现悲观锁需要使用数据库的锁机制
1.共享锁
2.排他锁
3.行锁

① 共享锁

共享锁也称为S锁,事务A获取S锁后,事务B也可以获得S锁,但获得该锁的事务只能读不能写
举个简单的例子来说就是租房子:很多人预约了同一个时间要去看同一个房子,这时一个人相当于一个事务,看房就相当于S锁,我可以来看房,别人也可以来看房这就是 共享锁。

② 排他锁

排他锁又称为X锁,事务A获得X锁后,事务B便不可以再获得S锁和X锁,事务A可以读写数据,直到事务A释放该锁,其他事务才能获得该锁。同样用上面的租房例子来说就是:我和中介签订了租房合同,其他人就不能再来看这个房子了,而我可以随意进出,其中签合同就是X锁,直到我的租期到了将钥匙还回去其他人才能来看房

注意这其中有个问题,当在读加锁的模式下,任何线程都可以对其进行读加锁的操作,但所有试图进行写加锁操作的线程都会被阻塞。直到所有读线程解锁。但是当读线程太多时,写线程一直被阻塞显然是不对的,所以一个线程想要对其进行写加锁时,就会阻塞读加锁,先让写加锁线程加锁。

悲观锁缺点
由于读写都需要加锁,会增大CPU的开销,拉低了执行效率

 

二、乐观锁

乐观锁是一种思想,不是实际意义上的加锁,认为当一个线程修改数据时认为其他线程不会修改该数据,但是在更新数据的时候会去判断以下其他线程是否修改了数据。通过版本来判断,如果数据被修改了就拒绝更新,之所以叫乐观锁是因为并没有加锁。

以下是2种实现方式
① 版本控制

乐观锁的实现十分简单,给表中加一个version字段。
例如:A和B分别使用同一个银行账户消费,原本账户余额为100元

  1. A要消费50元,此时A拿到的版本号为1
  2. 正准备修改时B进来读取了版本1的数据
  3. B要消费20元,此时B拿到的也是版本1
  4. 然后A执行了修改操作,余额变为50元,并将版本号改为2
  5. B拿到的版本1的数据还认为余额是100元,减去20剩下80元,并将拿到版本号改为2
  6. B再去执行修改操作,发现已经存在版本号为2的数据,说明已经被修改过了,就会驳回此次修改
  7. B重新去数据库读数据和版本再执行修改,直到修改成功
     
② CAS算法

CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。

CAS指令执行时,当且仅当内存V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

乐观锁缺点

ABA问题
线程1读取到的数据为666;
线程2将该数据修改为999;
线程2将该数据修改为666;
线程1对数据进行CAS操作
由于内存中数据仍然为666,因此CAS操作成功,但实际上该数据已经被线程2修改过了。这就是ABA问题。
表面上看上去似乎没什么影响,但是在特殊情况下,ABA却会带来隐患,例如栈顶问题:一个栈的栈顶经过两次(或多次)变化又恢复了原值,但是栈可能已发生了变化,对于此问题引入版本号就可以解决(AtomicStampedReference类)。比较时不仅比较内存中的数据有没有改便变,还比较版本号是不是一致,如果是就进行CAS操作。

三、总结

悲观锁阻塞事务,乐观锁回滚重试,它们各有优缺点,不要认为一种一定好于另一种。像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行重试,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值