乐观锁与悲观锁深入学习理解

  • 一、乐观锁和悲观锁
    • 悲观锁
    • 乐观锁
  • 二、两种锁的使用场景
    • 悲观锁
    • 乐观锁
  • 三、乐观锁实现方式
  • 四、乐观锁的缺点
  • 五、总结
  • 参考文献

一、乐观锁和悲观锁

乐观锁和悲观锁并不是真实存在的锁,而是一种设计思想,乐观锁和悲观锁对于理解 Java 多线程和数据库来说至关重要。

悲观锁

  1. 悲观锁是一种悲观思想,总认为最坏的情况可能会出现
  2. 悲观锁认为数据很可能会被其他人修改,所以悲观锁在持有数据时,会把资源锁住,这样其他的线程请求这个资源的时候就会阻塞,直到悲观锁把资源释放
  3. 传统的关系型数据库就用到了很多这种锁机制,如:行锁,表锁,读锁,写锁,Java中的悲观锁实现往往依靠这种数据库本身的锁功能实现
  4. Java中的synchronized和reentrantlock等独占锁(排它锁)也是一种悲观锁思想的实现,因为synchronized和reentrantlock不管是否持有资源,都会尝试去加锁

乐观锁

  1. 与悲观锁相反,乐观锁总认为资源不会被别人修改,所以读取不会上锁,但是乐观锁在进行写入操作时候会判断当前资源是否被修改过。
  2. 乐观锁的实现一般有两种方案:版本号机制和CAS:比较并替换实现。Java中的java.util.concurrent.atomic包下的原子变量类就是使用了CAS实现的。

二、两种锁的使用场景

悲观锁

select * from emp where emp_id=1 for update;
  1. 一般的,悲观锁不仅对写操作加锁,还会对读操作加锁。emp_id=1的记录被加锁之后,其他写操作在这个事务提交之前,都不会对这条数据进行操作,起到了独占、排他的作用。
  2. 在互联网的三高(高性能、高并发、高可用)环境下,悲观锁性能低,用的越来越少。

乐观锁

  1. 常见于读多写少的场景,即很少发横冲突的场景,这样可以省去锁的开销,增大系统吞吐量。
  2. 乐观锁的使用场景:如成本系统,员工要对一笔金额做修改,为了保证数据的准确性和实效性,使用悲观锁锁住某个数据后,再遇到其他需要修改数据的操作,那么此操作就无法完成金额的修改,而对产品来说是灾难性的一刻(读多的情况下,这条数据被锁住了),使用乐观锁的版本号机制能够解决这个问题。

三、乐观锁实现方式

采用版本号机制 和 CAS(compare and swap,比较并替换)算法实现。

  1. 版本号机制:通过在数据表的记录上加上version字段实现,表示数据被修改的次数,

思考的问题
1.在修改的那一刻,又要先读后写,这在多机器环境下如何控制?
我的理解:这让我联想到了redis的单线程getset,但原理不同,redis是单线程操作完成的。这里的最后一刻“先读后写”也没问题,乐观锁就是这么定义的,这个先读后写是使用update+where条件在一个事务中实现的,因此,多机器环境下在db层也不需要控制
2.数据库中执行update时用到锁了吗?3.事务与锁什么关系?
答:用到了,事务要更新数据对象时,先申请该对象的U锁。对象加了U锁,允许其他事务对它加S锁。在最后写入时,再申请将U锁升级为X锁。而不必在全过程中加X锁,参考:事务和锁。

理想场景下

(1) 男员工开启事务1
begin
update 表 set 金额=120, version = verison+1 where 金额=100 and version=0;
在没提交事务的前提下
(2) 女员工开启事务2
begin
update 表 set 金额=50, version = verison+1 where 金额=100 and version=0;
(3) 现在,当事务1先完成事务提交,版本号version=1,此时女员工再提交version=0的版本已经不在,提交失败,通知前端发起重试。

  1. CAS算法
    一种有名的无锁算法。即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)

    涉及的三要素:需要读写的内存值V、进行比较的值A、拟写入的新值B,当且仅当预期值A和内存值V相同时,将内存值修改为B,否则什么都不做

  2. JAVA对CAS的支持:在JDK1.5 中新添加 java.util.concurrent (J.U.C) 就是建立在 CAS 之上的。对于 synchronized 这种阻塞算法,CAS是非阻塞算法的一种实现。所以J.U.C在性能上有了很大的提升。

四、乐观锁的缺点

任何事情都是有利也有弊,乐观锁缺点如下:
  1. ABA问题:ABA 问题说的是,如果一个变量第一次读取的值是 A,准备好需要对 A 进行写操作的时候,发现值还是 A,那么这种情况下,能认为 A 的值没有被改变过吗?可以是由 A -> B -> A 的这种情况,但是 AtomicInteger 却不会这么认为,它只相信它看到的,它看到的是什么就是什么。JDK1.5之后,AtomicStampedReference的compareAndSet方法,首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以源自方式将该引用和标志的值设置为给定的更新值。也可以采用CAS的一个变种DCAS来解决这个问题。DCAS,是对于每一个V增加一个引用的表示修改次数的标记符。对于每个V,如果引用修改了一次,这个计数器就加1。然后再这个变量需要update的时候,就同时检查变量的值和计数器的值

  2. 循环开销大
    乐观锁在进行写操作时会判断是否能够写入成功,如果写入不成功将出发等待-重试机制,这种情况是一个自旋锁,适合于短期内获取不到,进行等待重试的锁,不适合长期获取不到锁的情况,另外,自旋循环对于性能来说开销比较大

  3. 实践记录【为了证明只使用数据库的u锁编程,是否存在并发隐患】

    两个java程序都加了所谓的事务处理,但是没有版本控制,在100个线程并发的情况下,原始数据=0,每个线程加1,看最终结果是否为100?

    第一种:

    结果:可以达到预期

    第二种


    很显然,第二种存在并发隐患,没有有效的控制读写

发现的其他问题及解决:mapper并不为空,但是发生了空指针,场景:多线程/并发下,除了一下参考的
1.关于在spring中使用多线程操作数据库时,遇到的mapper为null的问题
2.多线程时Autowired自动注入问题
3.解决多线程下@Autowired无法注入
4.Spring-Boot中如何使用多线程处理任务
如果还解决不了,那只能是依赖问题了(我是在github上下载了逆向工程),后来通过换依赖、删代码解决,依赖如下:

<!--MySQL JDBC驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.31</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.9</version>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><exclusions><exclusion><artifactId>spring-boot-starter-logging</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions><version>1.3.0</version>
</dependency>

五、总结

  1. CAS与synchronized的使用场景:CAS适用于“写少多读”场景(多读场景,冲突比较少,自旋概率低),synchronized适用于写比较多的场景。
  2. JavaSE 1.6之后,synchronized 的底层实现主要依靠 Lock-Free 的队列,基本思路是 自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和 CAS 类似的性能;而线程冲突严重的情况下,性能远高于CAS。
  3. synchronized 同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外消耗 cpu 资源;CAS 不需要切换线程,自旋概率小的场景下可以获得更高的性能。

参考文献

Java建设者公众号:看完你就应该能明白的悲观锁和乐观锁

乐观锁与悲观锁深入学习理解相关推荐

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

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

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

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

  3. 乐观锁与悲观锁深入学习

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

  4. mysql乐观锁与事务_[数据库事务与锁]详解七: 深入理解乐观锁与悲观锁

    注明: 本文转载自http://www.hollischuang.com/archives/934 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库 ...

  5. 深入理解乐观锁与悲观锁(实战)

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

  6. Java多线程学习总结(5)——乐观锁和悲观锁的基本概念、实现方式(含实例)、适用场景及常见面试题

     分享一个大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到人工智能的队伍中来!点击浏览教程 一.基本概念 乐观锁和悲观锁是两种思想,用于解决并发场景下的数据竞争问题. 乐观锁 ...

  7. 乐观锁和悲观锁的简单理解

    乐观锁和悲观锁的简单理解 一.悲观锁 每次读取数据时认为其他线程会修改这个数据,所以每次读的时候都会加锁,实现悲观锁需要使用数据库的锁机制 1.共享锁 2.排他锁 3.行锁 ① 共享锁 共享锁也称为S ...

  8. 一文彻底理解乐观锁与悲观锁

    通过阅读本文可以获得什么 1.什么是乐观锁? 2.乐观锁实现方式都有什么? 3.乐观锁优缺点有哪些? 4.乐观锁适用场景? 5.什么是悲观锁? 6.悲观锁实现方式有哪几种? 7.悲观锁优缺点? 8.悲 ...

  9. 浅谈对于乐观锁和悲观锁的理解

    今儿回忆了一下关于乐观锁和悲观锁的知识,要唠唠这两个点,那先得知道什么是锁? 1.锁的定义:锁是一种互斥的机制,在多线程环境中实现对资源的协调与控制,凡是有资源被多线程共享,涉及到修改的情况就要考虑锁 ...

最新文章

  1. 5大厂2020年应届生AI岗薪资一览,作为一个AI老鸟你的薪资有他们高吗?
  2. 认真看看, 以后写 SQL 就爽多了:MyBatis 动态 SQL:
  3. vue-admin模板第一次使用存在的坑
  4. Linux 面试最高频的 5 个基本问题!
  5. python保持登录状态_“保持登录状态”-最佳方法
  6. python中的seed_Python seed() 函数 - Python 教程 - 自强学堂
  7. SpringBoot2.0 基础案例(06):引入JdbcTemplate,和多数据源配置
  8. 【Kafka】Kafka WARN Failed to send SSL Close message
  9. restTemplate配置及使用
  10. 同步互斥阻塞 (2)
  11. 后PC时代中国半导体厂商的机会
  12. 【律联云知产课堂】商标注册需要什么条件?
  13. cgroup 分析之CPU和内存部分
  14. 浏览器字体和html字体,如何正确设置兼容浏览器的中文字体
  15. 离散数学复习--Modular Arithmetic
  16. 视频在线点播功能如何实现?
  17. java dispo lock_java实现文件上传和下载(1)
  18. mac 桌面不能右键 文件也不见了 但在finder的桌面上有
  19. 单源最短路径(dijkstra)
  20. 计算机专业考研电路原理,2019电子信息工程考研方向_电路与系统专业解读

热门文章

  1. 上班族加班漫画【转】
  2. 使用UltraISO或Rufus制作U盘启动安装优麒麟19.04
  3. 这个妞是什么?--Javascript中的new详解
  4. android微信自动化脚本,聊聊微信自动化的几种方案
  5. 关于ClownFish的问题
  6. Adams2013:MSC_LICENSE_FILE=27500@主机名
  7. 《芯片技术10讲》笔记01
  8. 计算机应用类专业综合,计算机应用类专业综合复习试题(二)(33页)-原创力文档...
  9. 走近中国的Oracle
  10. mysql回调地狱_浅谈JS回调地狱