前言:
乐观锁和悲观锁经常被询问到,在这里做一些总结
一.基础概念
乐观锁和悲观锁一般是用来维持事务的完整性.来解决并发场景中的数据竞争问题
- 乐观锁 :在对于数据的操作中十分的乐观,认为别人不会同时对自己的数据进行修改,所以乐观锁不会上锁,在执行更新的时候判断别人有没有修改自己的数据,如果别人有修改那么直接放弃操作,不然执行操作.
- 悲观锁 : 悲观锁在操作数据的时候比较悲观,认为别人会修改数据,所以直接将数据进行锁住,操作完成之后才释放锁 ,保证事务的完整性.
二.实现方式
- 乐观锁的实现方式主要有 CAS机制和版本号机制
CAS(Compare and swap)
- 需要读取的内存位置
- 进行比较的预期值
- 拟写入的新值
CAS操作逻辑: 如果内存位置等于预期的值,则将位置更新为新值,不然不进行操作.许多cas的操作是自旋的,如果操作不成功,会一直重新,知道操作成功
版本号机制
版本号机制的思路是在数据中增加一个字段,每当数据被修改的时候,版本号加1.当某个线程查询数据的时候,将数据的版本号一起查出来,当该线程更新数据的时候,判断当前版本号与之前是不是一致,如果一致进行操作
悲观锁是调用的时候进行加锁,使用完之后就不用加锁
三.优缺点和适用场景
1.功能限制
CAS只能保证单个变量的原子性,涉及到多个变量,cas是无能为例
2.竞争激烈程度
竞争不激烈的时候,乐观锁是更有优势的,加锁和解锁都需要消耗资源
竞争激烈的时候,悲观锁是更有优势的
3.乐观锁是不佳锁的,只是判断数据有没有被其他线程进行更新
四.CAS有哪些缺点?
1.ABA 问题
(1) 线程1读取内存数据为A
(2) 线程2将数据修改为B
(3) 线程2将数据修改为A
(4) 线程1进行CAS操作
这种情况的CAS会成功的,但是数据已经被修改过了 , 但是在一个栈顶经过两次或多次变化恢复原值,栈已经发生了变化
引入版本号,内存中的值发生一次变化,版本号都+1,进行CAS操作的时候,不仅仅需要比较内存中的值,还需要比较版本号的值,这样CAS才能成功
2.高竞争下的开销问题
并发冲突很大的情况下,CAS不断的进行自选,我们可以在这里进行一个阀门测试,如果重新尝试的次数超过一定的值会失败退出