欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

悲观锁和乐观锁-学习笔记

程序员文章站 2024-03-08 17:17:22
...

不管是乐观锁还是悲观锁,本质上都是程序安全中的内容.

通常情况下,可用加锁来保证要修改对象(或者是共享资源)的线程安全性(比如synchronized).,从而使得多个线程操作同一个对象而保证数据的有效性.

乐观锁和悲观锁
什么是悲观锁?什么是乐观锁?其实从字面上就可以区分出两者的区别,通俗点说

悲观锁:类似一个迫害妄想症患者,总是假设最坏的情况(同时有多个线程一起操作对象).所以每次处理数据都会上锁.直到处理结束.很明显会造成性能低下的问题

使用场景:数据库的很多地方都涉及到了这种机制,比如说行锁,表锁,读锁,写锁.Java程序里面的synchronized和ReentrantLock也是悲观锁的一种.

在数据库中,悲观锁的调用一般是在所要查询的语句后面加上 for update

select * from db_stock where goods_id = 1 for update

当有一个事务调用这条 sql 语句时,会对goods_id = 1 这条记录加锁,其他的事务如果也对这条记录做 for update 的查询的话,那就必须等到该事务执行完后才能查出结果,这种加锁方式能对读和写做出排他的作用,保证了数据只能被当前事务修改。

当然,如果其他事务只是简单的查询而没有用 for update的话,那么查询还是不会受影响的,只是说更新时一样要等待当前事务结束才行。

值得注意的是,MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交,就是说,如果我们不仅要读,还要更新数据的话,需要手动控制事务的提交,比如像下面这样:

set autocommit=0;
-- 开始事务
begin;
-- 查询出商品id为1的库存表数据
select * from db_stock where goods_id = 1 for update;
-- 减库存
update db_stock set stock_num = stock_num - 1 where goods_id = 1 ;
-- 提交事务
commit;

乐观锁:乐观锁只在一个时候检查数据是否被更新过,那就是在更新数据那一刻来判断.如果结果的预期和自己的预期一样的话,那么就可用正常更新数据.

CAS:CAS的全称是Compare-and-Swap.CAS是一种无锁思想.它假设线程对资源访问时没有冲突的(很明显大部分情况不可能).如果遇到冲突的话就使用一种叫做CAS的技术来鉴别线程冲突,如果冲突发生,就重试操作,直到没有冲突为止.

CAS有三个参数:V, A, B.V表示要读取内存的位置,A表示旧预期值,B表示新的值.
如图:

悲观锁和乐观锁-学习笔记
缺点:CAS算是比较高效的并发控制手段.不会阻塞其他线程.但是这样是存在问题的.如果C的预期结果和结果不一致的话,就会一直尝试,消耗CPU资源.

此外CAS只能操作一个共享变量的原子操作,如果要操作代码块的话,还是要synchronized.

此外最大的缺陷莫过于ABA问题.
前提:V是旧预计的值,B是新预计的值,这两个值一样才能进行修改.

这个逻辑有一个小缺陷:就是V的一开始的值为A,在准备修改为新值期间被另外一个线程修改成了B,后来又另外一个线程改了回去,经过两次操作当前线程会认为值没有被修改过,那么CAS操作会误认为该变量从来没有被修改过.这就是ABA问题.

那么要如何避免呢?

加一个标志位:version.每次操作就加一.或者用时间戳.

CAS一般用于读多写少的场景,因为这种场景线程冲突不会太多,CAS循环次数会有效降低,性能也更高.

版本号机制
版本号机制是数据库更新操作里非常实用的技巧,其实原理很简单,就是获取数据的时候会拿一个能对应版本的字段,然后更新的时候判断这个字段是否跟之前拿的值是否一致,一致的话证明数据没有被别人更新过,这时就可以正常实现更新操作。

相关标签: 数据库