一、并发控制

当程序中可能出现并发的情况时,就需要通过一定的手段来保证在并发情况下数据的准确性,通过这种手段保证了当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这种手段就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。

没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。

常说的并发控制,一般都和数据库管理系统(DBMS)有关。在DBMS中的并发控制的任务,是确保在多个事务同时存取数据库中同一数据时,不破坏事务的隔离性和统一性以及数据库的统一性。

实现并发控制的主要手段大致可以分为乐观并发控制和悲观并发控制两种。

首先要明确:无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像hibernate、tair、memcache等都有类似的概念。所以,不应该拿乐观锁、悲观锁和其他的数据库锁等进行对比。

二、悲观锁

​总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

举个栗子:

  1. 一间厕所在一个时间点内只能一个人使用(互斥锁),如果同时两个有“大事”要处理的人,他们一起冲到了厕所门口(当前厕所现在是没人的)
  2. 那个先冲进去厕所的人A,就会把门给关上(A上悲观锁:在查询数据成功后立马上锁)
  3. 上了锁之后,他才开始安心的处理他的大事(A更新数据)
  4. 而另一个人B,面对上锁的厕所,没有别的办法,也只能选择等待(B阻塞)
  5. 在A处理完之后,终于开锁了(A解除锁)
  6. 看到A出来,B立马就冲了进去,也上了锁,处理自己的大事(B查询数据,上锁,更新数据,解除锁)

上面的方法保证了每个人都能够公平竞争的使用厕所(谁先到谁就先处理数据),但是关门锁门的操作,让其他人只能等待某条数据处理完后,才能再去处理。这样就导致了数据处理的低效率

下面以淘宝下单过程中扣减库存的需求说明一下悲观锁的使用:

想要了解mysql的锁机制,可阅读这篇文章: 详解mysql的for update

而悲观锁还有一个极端的情况:你想在麦当劳里借个洗手间用用,但是你不知道洗手间在具体哪个位置,这时,你就超级过分地把整家麦当劳给锁了(表锁)再去慢慢找你的厕所。这样一来,不只是想来蹭厕所的客人,大量来点餐(进行其他数据操作)的客人也不能点了,好了麦当劳凉了你也凉了。

三、乐观锁

总是假设最好的情况,每次去读数据的时候都认为别人不会修改,所以不会上锁, 但是在更新的时候会判断一下在此期间有没有其他线程更新该数据, 可以使用版本号机制和CAS算法实现。 乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。

1、版本号机制

乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加不会减少。

举个栗子:

  1. 开课啦,教室里的学生们都是无比积极的
  2. 当老师提出第1个问题(版本号为1),就有几个学生争先恐后的举手想要回答问题(想要处理数据)
  3. 这时候,老师便让最先举手的那个学生回答第1题(版本号为1)
  4. 被点名的学生起来回答问题(某条数据处理成功进行),回答成功后,进入第2题(版本号改为了2)。
  5. 其余几个举手想回答第1题(想要处理版本为1)的学生知道看到老师现在提问的已经变成了第2题(版本号为2,跟自己的版本号对不上),就放下手了(想要处理的数据直接按照失败处理)。

这里需要注意一个点:学生的名字都应该是唯一的(版本号不重复),如果版本号重复,那么就失去意义了

当然,上面的例子是可以正常的处理事务,但会出现一个情况:很多近乎同时举手想要回答同一道题的学生都没被老师点名,这样就太打击学生的积极性了,怎么样才能够减小这种对学生的打击呢,最大程度的发挥学生的积极性呢?( 怎么减小乐观锁粒度, 最大程度的提升吞吐率,提高并发能力!)

假设老师采取另一种方式:只要你举手想要回答第n题,我只要还有回答时间,还有几次答同一道题的机会,就接着让你来回答,这样每个举手的学生就都能作答了。

问题表question有很多个问题(数据),第1道题在表中的question_id为1。每道题都有自己的剩余回答机会answer_num,如果被回答完,那么就不能再让人回答了。用sql表示如下:

update question set answer_num = answer_num - 1 where question_id = 1 and answer_num -1 > 0

以上update语句,在执行过程中,会在一次原子操作中自己查询一遍question_num(剩余问题)的值,并将其扣减掉1。

2、CAS算法

CAS(Compare-And-Swap,比较和互换),是一种有名的无锁算法。
CAS 算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。

无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步。
CAS算法涉及到3个操作数。

  • 需要读写的内存值V。
  • 进行比较的值A。
  • 拟写入的新值B。

当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

举个栗子:

  1. 桌上有 3 瓶雪碧(内存值为3)
  2. A想要开雪碧派对,确认了有 3 瓶雪碧(A读取时,内存值为 3 ),就出去打电话约朋友聚会(A事务进行较为缓慢)。
  3. 这期间,B想喝雪碧,看到了那 3 瓶雪碧(B读取时,内存值为3)
  4. 然后 B喝掉了 1 瓶,雪碧变成了 2 瓶(B事务执行完成,内存值变成了2)
  5. A约好朋友回来,确认雪碧变成了2瓶(内存值2,跟A读取时比较,发生变化了),雪碧派对开不成了,只能跟朋友说不聚了。(A事务执行失败,回滚)

B顺利执行了自己的事务,A执行自己事务失败回滚

3、CAS算法的ABA问题

CAS也并不完美,它存在**"ABA"问题**,假若一个变量初次读取是A,在compare阶段依然是A,但其实可能在此过程中,它先被改为B,再被改回A,而CAS是无法意识到这个问题的。CAS只关注了比较前后的值是否改变,而无法清楚在此过程中变量的变更明细,这就是所谓的ABA漏洞。

炒个上面的栗子:

  1. 桌上有 3 瓶雪碧(内存值为3)
  2. A想要开雪碧派对,确认了有 3 瓶雪碧(读取时,内存值为 3 ),就出去打电话约朋友聚会(事务进行较为缓慢)。
  3. 这期间,B想喝雪碧,看到了那 3 瓶雪碧(读取时,内存值为3)
  4. 然后B喝掉了 1 瓶,雪碧变成了 2 瓶(事务执行完成,内存值变成了2)
  5. 巧合发生了,B喝的雪碧得奖“再来一瓶”,B就立马换回来 1 瓶,然后放回了桌上,雪碧又变成了 3 瓶(内存值又被变成了3)
  6. A约好朋友回来,确认雪碧还是有3瓶(内存值还是3,跟读取时没有变化),就拿去开雪碧派对了(顺利执行自己的事务)。

虽然A和B都顺利执行了自己的事务,但这过程却存在了问题:

  • B白喝一瓶雪碧没给钱
  • A损失了一个“再来一瓶”

随着互联网三高架构(高并发、高性能、高可用)的提出,悲观锁已经越来越少的被应用到生产环境中了,尤其是并发量比较大的业务场景。

参考文章:什么是乐观锁,什么是悲观锁

乐观锁与悲观锁的生动举栗讲解相关推荐

  1. [初级]深入理解乐观锁与悲观锁

    2019独角兽企业重金招聘Python工程师标准>>> 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔 ...

  2. 乐观锁与悲观锁——解决并发问题

    引言   在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突.这就是著名的并发性问题. 典型的冲突有: 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失.例如: ...

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

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

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

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

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

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

  6. 深入理解乐观锁与悲观锁

    在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 乐观并发控制(乐观锁)和悲观并发控制(悲 ...

  7. 【转】数据库的乐观锁和悲观锁

    [转]数据库的乐观锁和悲观锁 有时候为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定.所以说,悲观锁和乐观锁主要是用来 ...

  8. 乐观锁、悲观锁简单分析,回忆旧(新)知识...

    2019独角兽企业重金招聘Python工程师标准>>> 今天被人问了下乐观锁和悲观锁,突然在脑子里好模糊,但又感觉以前很熟悉的东西竟然忘得这么干净.所以恶补加记录一下. 乐观锁和悲观 ...

  9. **Java有哪些悲观锁的实现_面试4连问:乐观锁与悲观锁的概念、实现方式、场景、优缺点?...

    推荐阅读: 数据库面试4连问:分库分表,中间件,优缺点,如何拆分? 终极手撕之架构大全:分布式+框架+微服务+性能优化,够不够? 消息队列面试,你能顶得住面试官这波10大连环炮的攻势吗? 01 乐观锁 ...

最新文章

  1. node mysql 批量写入_请问如何使用node.js在MySQL中进行批量插入
  2. PyTorch 之 Datasets
  3. 【struts2】名为dispatcher的ResultType
  4. Maven配置ali镜像
  5. 创业融资十项注意要点
  6. 如何将文件拷贝服务器上,如何将文件复制到云服务器上
  7. 印象笔记编辑pdf_做笔记就用印象笔记,支持录音做笔记( 附插件下载)
  8. Python模块学习 - 用tinify模块压缩和优化图片
  9. Unity运行时刻资源管理
  10. ajax上传json到服务器
  11. LIST函数JAVA特点_Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)...
  12. Web Components 系列(五)—— 详解 Slots
  13. 来尝个鲜,Python3.8几大新功能体验,冲鸭!
  14. json输出count如何计算_基于 Kafka + Flink + Redis 的电商大屏实时计算案例
  15. nacos启动闪退总结
  16. 兼具教育耐心和AI匠心,看「网易有道词典笔2.0」的变革者角色
  17. Android系统关于音量相关的控制-初识
  18. DELL笔记本UEFI+GPT安装window10与Ubuntu双系统
  19. matlab special,matlab的special函数用法
  20. 怎么关闭火狐浏览器的百度辅助模式(无障碍服务)

热门文章

  1. 流?I/O操作?阻塞?epoll?
  2. MOSFET正温度系数和负温度系数
  3. 让星星⭐月亮告诉你,打印完全格式的日期格式 包含 年月日时分秒毫秒
  4. 限时秒杀┃“探月计划”来袭,美国米德天文望远镜助孩子观月赏月
  5. 服务号和订阅号之间的区别,企业适合选择哪一个?
  6. [附源码]java毕业设计基于的高校学生考勤管理系统
  7. 图片旋转 90、180、270
  8. 蓄水池采样算法的python实现_蓄水池抽样及实现
  9. 微信步数C语言程序,获取微信步数 - osc_1v2pb1nt的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. 在直播软件搭建中,如何基于rtmp实现视频直播?