Posts /

乐观锁

06 Apr 2020

乐观锁


乐观锁和悲观锁的区别

乐观锁,并没有用到锁机制,而是通过数据本身来判断数据的正确性。也就是在执行业务前先查询数据,然后在执行完一段数据准备之后再判断版本是否有变化,从而判断数据插入是否有冲突。(版本也可以根据时间戳)

// 讲下乐观锁,乐观锁提交时怎么判断是否冲突

比如,对某一个数据进行操作。我们可以在字段上添加一个TX(交易ID[或者version],只能增加)。

--1. 
select (status,version) from table1 where id=#{id}
--2.执行业务逻辑,生成一条记录
--3.修改status为2
update table1
set status=2version=#{version}
where id=#{id} AND version=#{version};

优缺点

​ 乐观锁的思路是增加一个字段,通过该字段进行版本控制,从而判断冲突。乐观并发相信事务之间的数据竞争的概率比较小,因此尽可能直接做下去,等待需要提交的时候才锁定,所以不会死锁效率高一些。

乐观锁的缺点

​ 如果不是版本,用的是库存,会导致ABA的问题:

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间他的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没被修改过。这个问题被称为CAS操作的“ABA”问题。CAS(Compare-and-Swap)

​ 时间戳的方式,如果时间精度不够的话(例如:秒),一 秒间会发生读的瞬间的时间戳和其他用户更改完的时间戳一致的情况,这样还是会有脏读的风险。【所以需要数据竞争概率比较小的情况下】

自旋CAS(也就是不成功就一直循环直到成功)如果长时间不成功会给CPU带来非常大的执行开销。

CAS只对单个共享变量有效(当然数据库中的操作就是一个字段,不会存在多个共享变量的情况),当操作涉及跨多个共享变量时CAS无效。但是从JDK1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作。

悲观锁

悲观锁,采用了数据库中的锁机制,分为:共享锁(只读,不能写)、排他锁(加锁后其他线程不能读写)。

优缺点

​ 一开始就对数据加上锁,从而可以安心的做更新,为数据处理的安全提供了保证。但是同时也会增加“加锁解锁”的开销增加死锁的机会