乐观锁和悲观锁都是用于解决并发场景下的数据竞争问题,但是却是两种完全不同的思想。它们的使用非常广泛,也不局限于某种编程语言或数据库。

乐观锁的概念:

乐观锁:指的是在操作数据的时候非常乐观,乐观地认为别人不会同时修改数据,因此乐观锁默认是不会上锁的,只有在执行更新的时候才会去判断在此期间别人是否修改了数据,如果别人修改了数据则放弃操作,否则执行操作。

冲突比较少的时候, 使用乐观锁(没有悲观锁那样耗时的开销) 由于乐观锁的不上锁特性,所以在性能方面要比悲观锁好,比较适合用在DB的读大于写的业务场景。

悲观锁的概念:

悲观锁:指的是在操作数据的时候比较悲观,悲观地认为别人一定会同时修改数据,因此悲观锁在操作数据时是直接把数据上锁,直到操作完成之后才会释放锁,在上锁期间其他人不能操作数据

冲突比较多的时候, 使用悲观锁(没有乐观锁那么多次的尝试)对于每一次数据修改都要上锁,如果在DB读取需要比较大的情况下有线程在执行数据修改操作会导致读操作全部被挂载起来,等修改线程释放了锁才能读到数据,体验极差。所以比较适合用在DB写大于读的情况。

读取频繁使用乐观锁,写入频繁使用悲观锁。

乐观锁的实现方式:

乐观锁的实现方式主要有两种,一种是CAS(Compare and Swap,比较并交换)机制,一种是版本号机制

CAS机制:

CAS操作包括了三个操作数,分别是需要读取的内存位置(V)、进行比较的预期值(A)和拟写入的新值(B),操作逻辑是,如果内存位置V的值等于预期值A,则将该位置更新为新值B,否则不进行操作。另外,许多CAS操作都是自旋的,意思就是,如果操作不成功,就会一直重试,直到操作成功为止。

版本号机制:

版本号机制的基本思路,是在数据中增加一个version字段用来表示该数据的版本号,每当数据被修改版本号就会加1。当某个线程查询数据的时候,会将该数据的版本号一起读取出来,之后在该线程需要更新该数据的时候,就将之前读取的版本号与当前版本号进行比较,如果一致,则执行操作,如果不一致,则放弃操作。

悲观锁的实现方式:

悲观锁的实现方式也就是加锁,加锁既可以在代码层面(比如Java中的synchronized关键字),也可以在数据库层面(比如MySQL中的排他锁)。

乐观锁与悲观锁的优缺点和使用场景:

乐观锁和悲观锁并没有优劣之分,它们有各自适合的场景。

功能限制

乐观锁与悲观锁相比,适用的场景受到了更多的限制,无论是CAS机制还是版本号机制。

比如,CAS机制只能保证单个变量操作的原子性,当涉及到多个变量的时候,CAS机制是无能为力的,而synchronized却可以通过对整个代码块进行加锁处理;再比如,版本号机制如果在查询数据的时候是针对表1,而更新数据的时候是针对表2,也很难通过简单的版本号来实现乐观锁。

竞争激烈程度

在竞争不激烈(出现并发冲突的概率比较小)的场景中,乐观锁更有优势。因为悲观锁会锁住代码块或数据,其他的线程无法同时访问,必须等待上一个线程释放锁才能进入操作,会影响并发的响应速度。另外,加锁和释放锁都需要消耗额外的系统资源,也会影响并发的处理速度。

在竞争激烈(出现并发冲突的概率较大)的场景中,悲观锁则更有优势。因为乐观锁在执行更新的时候,可能会因为数据被反复修改而更新失败,进而不断重试,造成CPU资源的浪费。

乐观锁是否会加锁:

乐观锁本身是不加锁的,只有在更新的时候才会去判断数据是否被其他线程更新了,比如AtomicInteger便是一个例子。但是有时候乐观锁可能会与加锁操作合作,比如MySQL在执行更新数据操作的时候会加上排他锁。因此可以理解为乐观锁本身是不加锁的,只有在更新数据的时候才有可能会加锁。

CAS的缺点:

CAS的缺点主要有ABA问题、高竞争下的开销问题和本身的功能限制。

ABA问题

所谓的ABA问题,指的就是一个线程在操作数据的时候,有别的线程对数据进行了一系列操作,但是在该线程重新读取该数据的时候,被修改过的数据却和该线程一开始读取的数据一致,该线程不会知道该数据已经被修改过了,然后CAS操作就被判断是成功了。

ABA问题在一些场景下可能不会造成什么危害,但是在某些场景中却可能会造成隐患。比如CAS操作的是栈顶的数据,栈顶的数据虽然经过两次(或多次)变化后又恢复了原值,但是栈却可能是发生了变化,栈中数据的变化就可能会引发一些问题。

对于ABA问题,比较有效的方案是引入版本号。只要内存中的值发生变化,版本号就加1,在进行CAS操作的时候不仅比较内存中的值,也比较版本号,只有当二者都没有变化的时候,CAS操作才能执行成功。Java中的AtomicStampedReference类便是适用版本号来解决ABA问题的。

高竞争下的开销问题

在并发冲突的概率较大的高竞争场景下,如果CAS操作一直失败,就会一直重试,造成CPU开销大的问题。针对这个问题,一个简单的思路是引入退出机制,如果重试次数超过一定阈值,就强制失败退出。当然了,最好是避免在高竞争的场景下使用乐观锁。

自身的功能限制

CAS的功能是比较受限的,比如CAS只能保证单个变量(或者说单个内存值)操作的原子性。这就意味着原子性不一定能保证线程安全,当涉及到多个变量(或者说多个内存值),CAS也是无能为力。除此之外,CAS的实现需要硬件层面处理器的支持,在Java中普通的用户是无法直接使用的,只有借助atomic包下的原子类才能使用,灵活性有限。

@check_test

async def delete(self, *args, **kwargs):

result = []

uesr_id = self.get_session().get('id', 0)

ad_id = self.verify_arg_num(self.get_body_argument('ad_id'), 'id', is_num=True)

try:

await self.application.objects.get(Ad_Pools,id=ad_id,user_id=uesrid,is_delete=0)

try:

async with await DATABASE.transaction() as transaction:

# 查出用户对象

ad_obj = await Ad_Pools.use(transaction).select().where(Ad_Pools.id==ad_id,Ad_Pools.user_id==uesrid,Ad_Pools.is_delete==0).limit(1)[0]

query = (User_Pools.use(transaction).update({User_Pools.money: User_Pools.money + ad_obj.money})

.where(User_Pools.id == uesrid))

query_num = await query.execute()

if query_num == 0:

# 事务回滚

1/0

ad_obj.is_delete = 1

ad_obj.money = 0

# 如果这时候帖子的金额变更了 那进行回滚

await self.application.objects.get(Ad_Pools, id=ad_id, money=ad_obj.money)

await self.application.objects.update(ad_obj)

sucess, code, message = True, 0, '删除成功'

return self.send_msg(sucess, code, message, result)

except Exception as e:

sucess, code, message = False, 404, '删除失败,请重试'

return self.send_msg(sucess, code, message, result)

except Exception as e:

sucess, code, message = False, 404, '不存在'

return self.send_msg(sucess, code, message, result)

乐观锁与悲观锁的区别相关推荐

  1. 乐观锁和悲观锁区别以及使用场景

    乐观锁和悲观锁是并发控制中两种不同的策略,用于解决多个线程或进程同时访问和修改共享数据时可能出现的并发问题. 悲观锁 悲观锁的基本思想是,在数据被访问时,假设会有其他的线程或进程也会访问这个数据,所以 ...

  2. 面试题之——乐观锁和悲观锁区别

    对于乐观锁和悲观锁的区别及应用,要牢记一句话:读取频繁使用乐观锁,写入频繁使用悲观锁 本文转自:https://blog.csdn.net/L_BestCoder/article/details/79 ...

  3. Mysql乐观锁与悲观锁的区别

    原文地址: 05 mysql-乐观锁与悲观锁的区别 文章目录 1.悲观锁 2.乐观锁 3.两种锁的使用场景 4.乐观锁常见的两种实现方式 4.1. 版本号机制 4.2. CAS算法 5.乐观锁的缺点 ...

  4. 乐观锁和悲观锁的区别及使用场景

    转载自:https://blog.csdn.net/u010739551/article/details/81184203 悲观锁 悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每 ...

  5. java中的锁---乐观锁与悲观锁的区别

    锁,是开发中不得不掌握的一个知识点. 在面试中也会经常问到.其中乐观锁与悲观锁为最常见.首先介绍下两种锁 一:乐观锁: 1 介绍: 它的心态很好,每次别人使用它的时候,它会乐观的认为别人不修改数据,所 ...

  6. 乐观锁与悲观琐的区别

    锁.我们知道,最常用的处理多用户并发访问的方法是加锁.当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象.加锁对并发访问的影响体现在锁的粒度上.比如,放在一个表上的锁限制对整个表的并发访问 ...

  7. [精选]MySQL的各种锁(表锁,行锁,悲观锁,乐观锁,间隙锁,死锁)

    不少人在开发的时候,应该很少会注意到这些锁的问题,也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下),即使我们不会这些锁知识,我们的程序在一般情况下还是可以跑得好好的.因为数据库隐式帮我们 ...

  8. 乐观锁和悲观锁,可重入锁和不可重入锁(1)

    乐观锁和悲观锁,可重入锁和不可重入锁(1) 前言 感觉有一段时间没有写博客了呢.还是再接再厉吧,适当程度的总结能让我自己能够更加深入地巩固和理解自己所学习的一切. 还有,我很懒,而且我还是比较喜欢写日 ...

  9. Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景

    一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--悲观锁( ...

最新文章

  1. Dungeon Master(bfs)广度优先搜索
  2. k-means-algorithm
  3. 钉钉微应用的开发——主前端
  4. python之路_自定义属性、json及其他js补充
  5. Boxes in a Line UVA - 12657 (双向链表)
  6. 【转】Linux内存管理(最透彻的一篇)
  7. 网络编程之计算机网络的发展及基础网络概念
  8. Windows/Linux/Solaris 软中断处理机制
  9. sodp软件如何导入多个工作面信息_6款堪称业界良心的软件,好用到想为它们疯狂打call!...
  10. 浅谈数字证书安装证书原理
  11. setCookie时遇到的问题
  12. 关于顶级域名、二级域名、子域名的问题
  13. CSS3选择器(选择符)
  14. WPF 窗体最大化、最小化、还原 | WPF 最大化/最小化 按钮图标切换
  15. Javascript重命名对象键
  16. 关于“手机”一些术语的小总结
  17. [vue]开源一个精致完整的Vue项目(豆瓣)
  18. 北京楼市:一个打死都不说的秘密
  19. 国外30款设计精美的网站登录页面模板
  20. 中国电信服务器虚拟化,中国电信虚拟化软件集采:华为华三崛起,外资全面败退...

热门文章

  1. 出道即封神的ChatGPT,现在怎么样了?ChatGPT想干掉测试人员,做梦去吧
  2. openstack——Glance镜像服务
  3. 【定义并实现一个学生类(Student)】
  4. 长时间佩戴耳机的后果有多严重?选购适合的耳机太重要了
  5. Unity 如何启用MSAA?
  6. 重磅!深度学习神器 - 高层API 最强保姆级教程公开!
  7. UnityWebRequest.Post问题
  8. JSON 对象的这些操作和使用场景你知道多少?
  9. 网络上的计算机病毒怎么办,电脑中病毒怎么办  电脑病毒的危害有哪些
  10. OpenCV检测火焰