面试必备之乐观锁与悲观锁

开发 前端
悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的。

悲观锁和乐观锁并不是某个具体的“锁”而是一种并发编程的基本概念,是根据看待并发同步的角度;

悲观锁和乐观锁是用来解决并发问题的两种思想,在不同的平台有着各自的实现。

废话不多,开始讲解

1、悲观锁

  • 悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;
  • 特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;
  • 悲观锁通常多用于写多比较多的情况下(多写场景),避免频繁失败和重试影响性能

2、乐观锁

  • 乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现);
  • 特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁;
  • 乐观锁通常多于写比较少的情况下(多读场景),避免频繁加锁影响性能,大大提升了系统的吞吐量;

3、实现乐观锁算法

乐观锁一般会使用版本号机制或 CAS 算法实现

①版本号机制

数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会加一。当线程 A 要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功

②CAS 算法

CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新;

CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令

CAS 涉及到三个操作数:

  • V :要更新的变量值(Var)
  • E :预期值(Expected)
  • N :拟写入的新值(New)

当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值。如果不等,说明已经有其它线程更新了V,则当前线程放弃更新;

4、乐观锁问题

①ABA 问题

  • 一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 "ABA"问题;
  • ABA 问题的解决思路是在变量前面追加上版本号或者时间戳。JDK 1.5 以后的 AtomicStampedReference 类就是用来解决 ABA 问题的,其中的 compareAndSet() 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值

②循环时间长开销大

CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功。如果长时间不成功,会给 CPU 带来非常大的执行开

③只能保证一个共享变量的原子操作

CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5 开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作

责任编辑:姜华 来源: Android开发编程
相关推荐

2021-03-30 09:45:11

悲观锁乐观锁Optimistic

2024-01-29 01:08:01

悲观锁递归锁读写锁

2023-07-05 08:18:54

Atomic类乐观锁悲观锁

2011-08-18 13:44:42

Oracle悲观锁乐观锁

2019-04-19 09:48:53

乐观锁悲观锁数据库

2019-11-28 16:00:06

重入锁读写锁乐观锁

2009-09-25 16:43:44

Hibernate悲观Hibernate乐观

2020-09-16 07:56:28

多线程读写锁悲观锁

2019-05-05 10:15:42

悲观锁乐观锁数据安全

2019-01-04 11:18:35

独享锁共享锁非公平锁

2020-07-06 08:03:32

Java悲观锁乐观锁

2018-07-31 10:10:06

MySQLInnoDB死锁

2010-08-18 09:00:38

数据库

2023-08-17 14:10:11

Java开发前端

2020-10-22 08:21:37

乐观锁、悲观锁和MVC

2024-01-05 16:43:30

数据库线程

2019-10-23 10:15:04

MySQL数据库

2023-10-13 00:00:00

并发乐观锁CAS

2021-01-15 05:12:14

Java并发乐观锁

2021-05-25 05:25:24

MySQL数据库
点赞
收藏

51CTO技术栈公众号