乐观锁与悲观锁
一、概念介绍:
乐观锁(Optimistic Concurrency Control,缩写“OCC”),又叫做乐观并发控制,可以参考*-乐观并发控制:
(https://zh.wikipedia.org/wiki/%E4%B9%90%E8%A7%82%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6)
操作数据时非常乐观,认为别人不会同时修改数据。
因此乐观锁不会上锁,只是在执行更新的时候判断一下在此期间别人是否修改了数据;
适用场景:
当竞争不激烈 (出现并发冲突的概率小)时,乐观锁更有优势,
因为悲观锁会锁住代码块或数据,其他线程无法同时访问,影响并发,
而且加锁和释放锁都需要消耗额外的资源。
悲观锁(Pessimistic Concurrency Control,缩写“PCC”),又叫悲观并发控制,参考*-悲观并发控制:
https://zh.wikipedia.org/wiki/%E6%82%B2%E8%A7%82%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6
悲观锁在操作数据时比较悲观,认为别人会同时修改数据。
因此操作数据时直接把数据锁住,直到操作完成后才会释放锁;
上锁期间其他人不能修改数据,以便保证最大程度的独占性。
适用场景:
悲观并发控制主要用于数据竞争激烈的环境,
以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
因为乐观锁在执行更新时频繁失败,需要不断重试,浪费CPU资源。
备注:对于悲观锁来说,使用比较简单,只需要在使用的时候,加锁和解锁即可,这里不做详细介绍,Go里面的sync便是悲观锁的典型代表。
二、乐观锁的使用
对于乐观锁而言,主要有两种使用方法,一种是CAS,一种是版本号控制。
1. CAS
比较并交换(compare and swap, CAS),是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。
CAS操作包括了3个操作数:
1.需要读写的内存位置(V)
2.进行比较的预期值(A)
3.拟写入的新值(B)
操作逻辑:
如果内存位置V的值等于预期的A值,则将该位置更新为新值B,
否则不进行任何操作。许多CAS的操作是自旋的:如果操作不成功,会一直重试,
直到操作成功为止。
备注:CAS中的比较和交换两个操作,是由CPU支持的原子操作,其原子性是在硬件层面进行保证的。
go中实现CAS的例子:
// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
在:/ src / runtime / internal / atomic / asm_amd.s文件中
TEXT runtime∕internal∕atomic·Cas64(SB), NOSPLIT, $0-25
MOVQ ptr+0(FP), BX
MOVQ old+8(FP), AX
MOVQ new+16(FP), CX
LOCK // 可以锁住总线保证多次内存操作的原子性
CMPXCHGQ CX, 0(BX) // 如果AX与BX相等,则CX送BX且ZF置1;否则BX送CX,且ZF清0
SETEQ ret+24(FP)
RET
go中的sync/automic 就是采用了乐观锁实现的。
CAS的主要缺点:
1.在竞争激烈的时候,每个线程/协程都是自旋的,影响效率。
2.无法处理ABA的问题,例如:协程1和协程2
1)协程1读取数据为A
2)协程2读取数据为A,并改成了B
3)协程1对数据进行CAS操作,
备注:虽然还是能修改数据,但是实际上当中已经被修改过多次了,特别是涉及到栈的操作的时候,就会出问题。
2.版本号控制
版本号机制的基本思路是在数据中增加一个字段version,表示该数据的版本号,每当数据被修改,版本号加1。当某个线程查询数据时,将该数据的版本号一起查出来;当该线程更新数据时,判断当前版本号与之前读取的版本号是否一致,如果一致才进行操作。当然这里的版本号也可以是时间戳或其他的字段。
(图片来自:https://www.cnblogs.com/dugo/p/12195135.html)
三、参考资料
https://www.cnblogs.com/kismetv/p/10787228.html
https://www.cnblogs.com/dugo/p/12195135.html
https://zhuanlan.zhihu.com/p/159334753
(作者的话:公众号比较新的缘故,文章不能评论,可以私信给作者:aaa@qq.com; 如果喜欢还请点赞或者分享,算是对笔者的一点鼓励~)
正在收集公众号后续知识方向,欢迎大家投票。