悲观锁和乐观锁是从是否锁住资源的角度进行分类的。

悲观锁

悲观锁比较悲观,它认为如果不锁住这个资源,别的线程就会来争抢,就会造成数据结果错误,所以悲观锁为了确保结果的正确性,会在每次获取并修改数据时,都把数据锁住,让其他线程无法访问该数据,这样就可以确保数据内容万无一失。

这也和我们人类中悲观主义者的性格是一样的,悲观主义者做事情之前总是担惊受怕,所以会严防死守,保证别人不能来碰我的东西,这就是悲观锁名字的含义。

乐观锁

乐观锁比较乐观,认为自己在操作资源的时候不会有其他线程来干扰,所以并不会锁住被操作对象,不会不让别的线程来接触它,同时,为了确保数据正确性,在更新之前,会去对比在我修改数据期间,数据有没有被其他线程修改过:如果没被修改过,就说明真的只有我自己在操作,那我就可以正常的修改数据;如果发现数据和我一开始拿到的不一样了,说明其他线程在这段时间内修改过数据,那说明我迟了一步,所以我会放弃这次修改,并选择报错、重试等策略。

这和我们生活中乐天派的人的性格是一样的,乐观的人并不会担忧还没有发生的事情,相反,他会认为未来是美好的,所以他在修改数据之前,并不会把数据给锁住。当然,乐天派也不会盲目行动,如果他发现事情和他预想的不一样,也会有相应的处理办法,他不会坐以待毙,这就是乐观锁的思想。

乐观锁的实现一般都是利用 CAS 算法实现的。我们举个例子,假设线程 A 此时运用的是乐观锁。那么它去操作同步资源的时候,不需要提前获取到锁,而是可以直接去读取同步资源,并且在自己的线程内进行计算。

而假设此时的同步资源已经被其他线程修改更新了,线程 A 会发现此时的数据已经和最开始拿到的数据不一致了,那么线程 A 不会继续修改该数据,而是会根据不同的业务逻辑去选择报错或者重试。

悲观锁和乐观锁概念并不是 Java 中独有的,这是一种广义的思想,这种思想可以应用于其他领域,比如说在数据库中,同样也有对悲观锁和乐观锁的应用。

典型案例
悲观锁:synchronized 关键字和 Lock 接口
Java 中悲观锁的实现包括 synchronized 关键字和 Lock 相关类等,我们以 Lock 接口为例,例如 Lock 的实现类 ReentrantLock,类中的 lock() 等方法就是执行加锁,而 unlock() 方法是执行解锁。处理资源之前必须要先加锁并拿到锁,等到处理完了之后再解开锁,这就是非常典型的悲观锁思想。

乐观锁:原子类
乐观锁的典型案例就是原子类,例如 AtomicInteger 在更新数据时,就使用了乐观锁的思想,多个线程可以同时操作同一个原子变量。

大喜大悲:数据库
数据库中同时拥有悲观锁和乐观锁的思想。例如,我们如果在 MySQL 选择 select for update 语句,那就是悲观锁,在提交之前不允许第三方来修改该数据,这当然会造成一定的性能损耗,在高并发的情况下是不可取的。

相反,我们可以利用一个版本 version 字段在数据库中实现乐观锁。在获取及修改数据时都不需要加锁,但是我们在获取完数据并计算完毕,准备更新数据时,会检查版本号和获取数据时的版本号是否一致,如果一致就直接更新,如果不一致,说明计算期间已经有其他线程修改过这个数据了,那我就可以选择重新获取数据,重新计算,然后再次尝试更新数据。

SQL语句示例如下(假设取出数据的时候 version 为1):

UPDATE student
    SET 
        name = ‘小李’,
        version= 2
    WHERE   id= 100
        AND version= 1

“汝之蜜糖,彼之砒霜”

有一种说法认为,悲观锁由于它的操作比较重量级,不能多个线程并行执行,而且还会有上下文切换等动作,所以悲观锁的性能不如乐观锁好,应该尽量避免用悲观锁,这种说法是不正确的。

因为虽然悲观锁确实会让得不到锁的线程阻塞,但是这种开销是固定的。悲观锁的原始开销确实要高于乐观锁,但是特点是一劳永逸,就算一直拿不到锁,也不会对开销造成额外的影响。

反观乐观锁虽然一开始的开销比悲观锁小,但是如果一直拿不到锁,或者并发量大,竞争激烈,导致不停重试,那么消耗的资源也会越来越多,甚至开销会超过悲观锁。

所以,同样是悲观锁,在不同的场景下,效果可能完全不同,可能在今天的这种场景下是好的选择,在明天的另外的场景下就是坏的选择,这恰恰是“汝之蜜糖,彼之砒霜”。

因此,我们就来看一下两种锁各自的使用场景,把合适的锁用到合适的场景中去,把合理的资源分配到合理的地方去。

两种锁各自的使用场景
悲观锁适合用于并发写入多、临界区代码复杂、竞争激烈等场景,这种场景下悲观锁可以避免大量的无用的反复尝试等消耗。

乐观锁适用于大部分是读取,少部分是修改的场景,也适合虽然读写都很多,但是并发并不激烈的场景。在这些场景下,乐观锁不加锁的特点能让性能大幅提高。

引用:https://kaiwu.lagou.com/course/courseInfo.htm?courseId=16#/detail/pc?id=258

Java多线程学习十二:悲观锁和乐观锁的本质||相关推荐

  1. Java多线程学习十二: synchronized的工作原理 以及背后的“monitor 锁”

    我们研究下 synchronized 背后的 monitor 锁. 获取和释放 monitor 锁的时机 我们都知道,最简单的同步方式就是利用 synchronized 关键字来修饰代码块或者修饰一个 ...

  2. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  3. Java多线程学习十五:公平锁和非公平锁,为什么要“非公平”?

    什么是公平和非公平 公平锁 指的是按照线程请求的顺序,来分配锁: 非公平锁 指的是不完全按照请求的顺序,在一定情况下,可以允许插队.但需要注意这里的非公平并不是指完全的随机,不是说线程可以任意插队,而 ...

  4. Java多线程学习十九:JVM 对锁进行了哪些优化?

    JVM 对锁进行了哪些优化呢? 相比于 JDK 1.5,在 JDK 1.6 中 HotSopt 虚拟机对 synchronized 内置锁的性能进行了很多优化,包括自适应的自旋.锁消除.锁粗化.偏向锁 ...

  5. Java多线程学习十四:Lock 有哪几个常用方法?分别有什么用?

    Lock 接口是 Java 5 引入的,最常见的实现类是 ReentrantLock,可以起到"锁"的作用. Lock 和 synchronized 是两种最常见的锁,锁是一种工具 ...

  6. Java多线程学习十六:读写锁 ReadWriteLock 获取锁有哪些规则

    读写锁 ReadWriteLock 获取锁有哪些规则呢? 在没有读写锁之前,我们假设使用普通的 ReentrantLock,那么虽然我们保证了线程安全,但是也浪费了一定的资源,因为如果多个读操作同时进 ...

  7. Java多线程(十二)之线程池深入分析(下)

    一.数据结构与线程构造方法 由于已经看到了ThreadPoolExecutor的源码,因此很容易就看到了ThreadPoolExecutor线程池的数据结构.图1描述了这种数据结构. 图1 Threa ...

  8. Java多线程学习(二)

    2019独角兽企业重金招聘Python工程师标准>>> 非线程安全:在多个线程对同一个对象中的实例进行并发访问时发生,产生的后果即为脏读. 线程安全:获得的实例变量的值是经过同步处理 ...

  9. Java多线程学习十:线程池实现“线程复用”的原理

    线程复用原理 我们知道线程池会使用固定数量或可变数量的线程来执行任务,但无论是固定数量或可变数量的线程,其线程数量都远远小于任务数量,面对这种情况线程池可以通过线程复用让同一个线程去执行不同的任务,那 ...

最新文章

  1. rotate.js实现图片旋转 (chrome,IE,firefox都可以实现)
  2. python画动态爱心-使用Python画出小人发射爱心的代码
  3. java.lang包 下
  4. C++ STL泛型编程——在ACM中的运用
  5. centos 6.5 配置LDAP服务器+客户端!
  6. java中8进制常量_下列关于Java语言简单数据类型的说法中,正确的一项是______。A.以0开头的整数代表8进制整型常量B....
  7. 发布几个PDF小工具
  8. oracle 11g安装时提示environment variable:PATH 失败
  9. Web前端开发人员和设计师必读文章推荐【系列六】
  10. NEC学习 ---- 模块 - 带点文字链接列表
  11. QT5+ROS程序开发
  12. 数据库——Oracle(1)
  13. 差点无缘Offer!java开发和运行环境实验报告
  14. 数据结构—二叉树,满二叉树、完全二叉树、二叉树的性质(思维导图)
  15. 二本华南师范计算机考研,经验贴|19华师软工专硕|初试415,二本逆袭211
  16. 最新支持备案域名后缀列表
  17. 入侵mssql2000
  18. 有关H5第八章的页面布局与规划介绍
  19. view标签class属性
  20. 基于Unity开发实现的坦克游戏设计

热门文章

  1. 中国移动中国联通中国电信 三家运营商公布首批5G城市名单
  2. 雷军自曝25年前旧照 网友发现端倪:25年前就有MIX 2S了?
  3. Java成神之路——CountDownLatch、CyclicBarrier
  4. Unity界面插件NGUI基础教程
  5. GridView xml中设置android:focusable=false无效的原因
  6. 获取本机IP和MAC地址
  7. mysql nodejs 并发
  8. 我的docker随笔16:构建一个特定的nodejs镜像
  9. ubuntu10.04添加账户示例
  10. 【flink】flink 消费组死掉 Lag不变 kafka不提交 重启恢复 非常诡异